Version 1.3 of ai05s/ai05-0045-1.txt

Unformatted version of ai05s/ai05-0045-1.txt version 1.3
Other versions for file ai05s/ai05-0045-1.txt

!standard 9.2(6)          07-06-13 AI05-0045-1/02
!class binding interpretation 07-04-04
!status work item 07-04-04
!status received 07-04-02
!priority Medium
!difficulty Medium
!qualifier Omission
!subject Termination of unactivated tasks
!summary
Tasks that are created but never activated become terminated when their master is finalized.
!question
9.2(6) says
Should the task that created the new tasks never reach the point where it would initiate the activations (due to an abort or the raising of an exception), the newly created tasks become terminated and are never activated.
A return statement for a type containing tasks that is left without being completed is going to leave the master of those tasks. But the activation point (at the site of the function call) could very well still be executed, for instance if there is a local handler. Moreover, an extended_return can be left by an exit or goto statement (not one of these cases listed in this sentence), and such exits will occur mainly in cases where the activation point will in fact be reached. How can the master being left wait for tasks that have not yet been activated, but are not terminated because this paragraph does not apply?
!recommendation
(See Summary.)
!wording
Replace 9.2(6) by:
If a master includes (directly or indirectly) the creation of a task, and it begins finalization prior to either initiating the activation of the new task or returning the task to a caller as part or coextension of a return object, the new task is never activated and becomes terminated.
AARM Note: A master could indirectly include the creation of a task if the task is returned to it from a function call. This rule covers such tasks that aren't activated.
A created task might not be activated because of an exception being raised, because of an abort, or because of a transfer of control out of an extended_return_statement.
!discussion
What is important here is what happens if the master of unactivated tasks is finalized (as then we would need to wait for them). We don't care why the tasks were never activated. Thus, the original wording of 9.2(6) says too much. It also has a race condition, in that if the task is aborted when it reaches the activation point but before the tasks actually are activated, this rule doesn't seem to apply.
The wording also is not clear about precisely when the tasks become terminated. The tasks being terminated on creation would meet the requirements of this rule. Thus, the value of T'Terminated is not well-defined for such tasks. That seems bad.
Because of all of these reasons, we've rewritten the rule in terms of finalization rather than "reaching the point of activation".
!corrigendum 9.2(6/2)
Replace the paragraph:
Should the task that created the new tasks never reach the point where it would initiate the activations (due to an abort or the raising of an exception), the newly created tasks become terminated and are never activated.
by:
If a master includes (directly or indirectly) the creation of a task, and it begins finalization prior to either initiating the activation of the new task or returning the task to a caller as part or coextension of a return object, the new task is never activated and becomes terminated.
!ACATS test
An ACATS C-Test should be created to check the extended_return case with a transfer of control:
return A : Type_with_a_Task do
... goto Oops; ...
end return;
<<Oops>> return B : Type_with_a_Task do ... end return;
!appendix

From: Stephen W Baird
Date: Monday, April 2, 2007 11:48 AM

9.2(6) says
    Should the task that created the new tasks never reach the point
    where it would initiate the activations (due to an abort or the raising
    of an exception), the newly created tasks become terminated and
    are never activated.

The parenthesized part needs updating for Ada05 to handle the case
where an extended return statement for a function whose result type
contains tasks is exited via either an exit statement or a goto statement.

****************************************************************

From: Randy Brukardt
Date: Monday, April 2, 2007  9:40 PM

I'm begining to wonder if this isn't a real hole. Consider something like:

    return A : Type_with_a_Task do
        ...
        goto Oops;
        ...
    end return;

    <<Oops>>
    return B : Type_with_a_Task do
        ...
    end return;

In a case like this, the rule you note doesn't apply, as the "point at which the
tasks are activated" is after the call in the caller of the function. That surely
WILL be executed. But we still don't want the first set of tasks activated. Indeed,
the rule is perfectly OK as originally written, as any case where the activation point
isn't executed will be due to an exception (Program_Error).

So I think we may need a new rule (or a reconsideration of the master rules, or
something), which means a new AI. Sigh.

****************************************************************

From: Stephen W Baird
Date: Tuesday, April 3, 2007  3:55 PM

I don't think there is a new problem here.

The wording of 9.2(6) is terrible, but we've lived with it for years.

Taken literally, the wording is ridiculous: "at the point where something 
will never happen, do something", or something like that.

If, strictly speaking, this is defined at all (a point I am not willing to 
concede), it certainly requires a crystal ball - do or do not do something now 
depending on what is going to happen in the future. This reminds me of the old
definition of the Max_Size_In_Storage_Units attribute ("the maximum value ...
that will be requested ...").

What this wording really means is "at the point where the task activation point
becomes unreachable, terminate the new tasks".

I think this interpretation makes as much sense in Ada05 as it did in Ada95.

In your example, the set of tasks associated with a different extended return
statement (or even a different elaboration of the same extended return statement)
is a different set. Thus, the task activation point for the original set of 
tasks is no longer reachable after the goto statement and 9.2(6) does apply.

****************************************************************

From: Randy Brukardt
Date: Monday, April 2, 2007  4:57 PM

> If, strictly speaking, this is defined at all (a point I am not willing to 
> concede), it certainly requires a crystal ball - do or do not do something now 
> depending on what is going to happen in the future. This reminds me of the old
> definition of the Max_Size_In_Storage_Units attribute ("the maximum value ...
> that will be requested ...").

This is not the way I read this paragraph (with one small exception). It says that
if you never reach the activation point, the tasks are never activated. That doesn't
require a crystal ball. The part about "become terminated" is does have the problem
you are talking about, but it is irrelevant until you reach the task waiting point.

Here, you *are* going to reach the task activation point, but you *still* don't want
to activate the tasks. That is completely different.

> What this wording really means is "at the point where the task activation 
> point becomes unreachable, terminate the new tasks".

Maybe. I'm not sure; I always thought it meant "if you reach the task waiting point
(master destruction point), then any unactivated tasks are terminated". Maybe it doesn't
matter in Ada 95 tasks.

> I think this interpretation makes as much sense in Ada05 as it did in Ada95.

For Ada 95, you may be right. But I don't see this for extended returns.

> In your example, the set of tasks associated with a different extended return statement
> (or even a different elaboration of the same extended return statement) is a
> different set.

So what? They're both activated at the same point. And, as a practical matter, they're
going to be on the same ring (list): we're only going to pass one in to take the tasks!

> Thus, the task activation point for the original set of tasks
> is no longer reachable after the goto statement and 9.2(6)does apply.

Huh? It hasn't changed (its the point of the call), and it is surely still going to be
executed.

Anyway, the object is finalized when its (local) master goes away (3.10.2(10.1/2) says that
the master is the return statement until that statement completes). But that makes a
totally new situation: one where the activation point of the tasks is after the master
of the tasks is finalized. Keep in mind that there isn't any wording like 3.10.2(10.1/2)
that applies to the activation point: it never changes its location! So it is initially
outside of the master of the object; then when the return happens, the master changes so
that the activation point is inside as usual. 

In an example like this, it's clear that the tasks are gone before the activation point
is reached, but it surely will be reached.

In any case, my concerns and your statement that the original wording
is bad means this is *not* a minor wording change. I don't want that AI derailed because
people are arguing about the meaning of rules, or are just plain confused. So we need an AI.

And given that we're rewording this paragraph anyway, we might as well fix your concern.
I'd dump all of the verbiage and simply say what is important: "if a task has not been
activated when the master on which it depends is waiting for the termination of dependent
tasks, the task becomes terminated and is never activated." This probably wasn't done
because it is a forward reference (and it might make the most sense in 9.3) [although
I note that "terminated" is also a forward reference]. That would drop all of the rot
about why that might happen, which is a certain maintenance problem. If we moved this
to 9.3 (after paragraph 5), then it might make sense to make the current wording a note
in 9.2; then it doesn't have to be exactly right.

****************************************************************

From: Stephen W Baird
Date: Wednesday, April 4, 2007  3:24 PM

This might be ok, but it makes me nervous because it doesn't quite
correspond to how most implementations would want to implement this.

The "typical" implementation that I envision involves terminating these
tasks while still still in the callee. The caller is unaware that these
tasks were ever created and is certainly not responsible for terminating
them. In this example,

     function F return R is
     begin
       return R'(Some_Task_Component => <>,
                 Some_Natural_Component => Ident_Int (-1),
                 Some_Other_Task_Component => <>);
     exception
        when Constraint_Error =>
           Do_A_Bunch_Of_Stuff;
           return ... ;
     end F;

, any tasks that were created as part of the aggregate evaluation before
Constraint_Error was raised would be terminated before, not after,
the call to Do_A_Bunch_Of_Stuff.

According to the wording you suggest, they would be terminated after.

This would be ok if we are really sure that there is no way that anyone
could tell the difference.

I can't think of any way that anyone could tell the difference, but that
is a much weaker statement.

This is my only objection. I like the simplicity of the wording you
propose. Perhaps it could be augmented with an explicit implementation
permission to terminate the tasks earlier; or, at the very least, an
AARM note indicating that this was the intent.

What do you think?

****************************************************************

From: Randy Brukardt
Date: Monday, April 2, 2007  9:40 PM


> This might be ok, but it makes me nervous because it doesn't quite
> correspond to how most implementations would want to implement this.

That's possible; I have no idea how a "typical" implementation works. Indeed,
I don't think such a thing exists; implementations seem to vary widely here.

> The "typical" implementation that I envision involves terminating these
> tasks while still still in the callee. The caller is unaware that these
> tasks were ever created and is certainly not responsible for terminating
> them. In this example,
> 
>      function F return R is
>      begin
>        return R'(Some_Task_Component => <>,
>                  Some_Natural_Component => Ident_Int (-1),
>                  Some_Other_Task_Component => <>);
>      exception
>         when Constraint_Error =>
>            Do_A_Bunch_Of_Stuff;
>            return ... ;
>      end F;
> 
> , any tasks that were created as part of the aggregate evaluation before
> Constraint_Error was raised would be terminated before, not after,
> the call to Do_A_Bunch_Of_Stuff.
> 
> According to the wording you suggest, they would be terminated after.

No, that's wrong. The master of a return statement is the statement itself
(see 3.10.2(10.1/2), which defines the accessibility which then defines the master).
If the return statement is successfully completed, then the master is changed to
that of the caller of the function. It's never the function itself.

So, in this case, when the exception is raised, the return statement is not completed,
and the master that is the return statement is left. That means that the created but
unactivated tasks will be awaited, and that will trigger the new rule and make them
terminated.

Now, I realize that you may not actually implement a "real" master for a return
statement, and simply have the incomplete exit of the return statement finalize and
terminate the tasks. And that should be equivalent. But that's not how RM wording works.

I realize that remembering that return statements are temporary masters is hard to do
(even though it comes from a model that you originated), but it's the rule. Note that
controlled finalization happens here, too, not after the exception handler. (Which is
very ugly for us, but whatever.)

> This would be ok if we are really sure that there is no way that anyone
> could tell the difference.

There is no difference, so I don't think you can tell it. ;-)
 
> I can't think of any way that anyone could tell the difference, but that
> is a much weaker statement.

True, but I don't see how you could get a different result.

> This is my only objection. I like the simplicity of the wording you
> propose. Perhaps it could be augmented with an explicit implementation
> permission to terminate the tasks earlier; or, at the very least, an
> AARM note indicating that this was the intent.
> 
> What do you think?

I don't think you need to terminate the tasks earlier (or can, for that matter), because
the moment when you start awaiting the master is essentially the same moment when you
know that the activation point won't be executed/the return statement will not be
completed.

The only thing that I think might be incompatible is the precise instant when
T'Terminated becomes true. Your reading of the wording seems to make that
retroactive(!) - I think we're better off with the  wording clear that isn't ever the case.

So I think you've convinced me that I'm on the right track. It certainly is simpler than
talking about executing the point of task activation. I'll write it up this way and then
the full ARG can tear it to bits... 

****************************************************************


Questions? Ask the ACAA Technical Agent