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

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

!standard 9.2(6)          07-08-06 AI05-0045-1/03
!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 return statement is left such that the return object is not returned to the caller, any task that was created as part or coextension of the return object immediately becomes terminated and is never activated [irregardless of where it would have been activated]. Otherwise, if a master that directly encloses the point where the activation of a task T would be initiated begins finalization before the activation of T is initiated, T becomes terminated and is never activated.
AARM Notes: The first case can happen with an exception being raised in a return statement, by an exit or goto from an extended_return_statement, or by a return statement being aborted. Any tasks created for the return object of such a return statement are never activated.
The second case can only happen if the activation point of T is not reached due to an exception being raised or the task being aborted. Note that this is exclusive; if the master is being finalized, we're already past the activation point and since Ada doesn't have any restart semantics, we can never get there. End AARM Notes.
[Editor's note: The square brackets here mark redundant text.]
!discussion
What is important here is what happens if the master that directly encloses the activation point of any unactivated tasks is finalized. We don't care why the tasks were never activated or where they were created. 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 of a master rather than "reaching the point of activation".
While the most critical point is when the master of the tasks begins to finalize (because we would need to wait for the unactivated tasks at that point), writing the rule that way could cause unactivated tasks to hang around in a limbo state for a long time. (Consider an allocator for an access-to-task that is declared at library level; the master is that of the environment task.) Thus we talk about the master of that encloses the activation point of the task.
We need a special rule for a return statement which is completed without returning to the caller (a "failed" return), because the activation point of those tasks may be executed if the function executes a second return statement successfully. In that case, the tasks created by the failed return should never be activated, while the second set created by the later successful return do need to be activated. This doesn't fit cleanly into the model.
!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 return statement is left such that the return object is not returned to the caller, any task that was created as part or coextension of the return object immediately becomes terminated and is never activated irregardless of where it would have been activated. Otherwise, if a master that directly encloses the point where the activation of a task T would be initiated begins finalization before the activation of T is initiated, T becomes terminated and is never activated.
!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... 

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

From: Randy Brukardt
Date: Wednesday, July 18, 2007  8:02 PM

The wording proposed in Paris for this AI is to replace 9.2(6) with the
following:

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.

However, I don't think this wording works; it has the same problem with the
example Steve gave at the meeting as my original proposal.

Steve's example was:

procedure P is
   type Ref is access T_has_Task;
   Ptr : Ref;
   task Child;
   task body Child is
   begin
      Ptr := new T_has_Task;
   end Child;
begin
   null;
end P;

The allocator is clearly "indirectly" included in the master of P (as it is
the master of Child). If that master begins finalization during the
execution of the allocator (and before the activation of the task), the task
would be "brutally killed" (as Pascal put it in his notes on the minutes).
But Child is still running and presumably is expecting the task to be
working.

The reason that the "indirectly" was added was so that the outer master that
is handled the return object is also one that "includes the creation of a
task". But this is clearly too broad. I think we need to drop the
"indirectly" and address the case of return objects explicitly.

Something like:

If a master directly includes 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. For the purposes of
this rule, a return object which is returned normally is deemed to have been
directly included in the master surrounding the point of call (see 3.10.2).

I'm not very thrilled with this wording, as it introduces a special case.
OTOH, a return object is the only case where the master of an object changes
after it is created, so it seems likely that it is the only special case
that need be handled.

Tucker *always* has a better idea about wording (even if it is wrong, as in
the wording proposed at the meeting), so I'm hoping that he has a better
idea that actually works. (The rest of you can make suggestions as well, of
course.)

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

From: Tucker Taft
Date: Wednesday, July 18, 2007  9:08 PM

I'm not convinced there is a problem, because I don't believe
the execution of a subtask is *included* in the execution of
the parent task.  We could be more explicit by clearly limiting
this to masters within the creating task, if we really felt
there was a danger of confusion.  Hence:

   If a task T1 creates a task T2, and if a master within task T1
   that includes (directly or indirectly) the creation of the task T2
   begins finalization prior to either initiating
   the activation of task T2 or returning task T2 to a
   caller as part or coextension of a return object, task T2
   is never activated and becomes terminated.

But as indicated above, I think the Paris wording is fine, because
of the use of the phraseology "master includes," which really means
"the execution of the master construct includes."  I don't think
we have in the past implied that a task "includes" the execution of its
subtasks.

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

From: Randy Brukardt
Date: Wednesday, July 18, 2007 10:00 PM

>    If a task T1 creates a task T2, and if a master within task T1
>    that includes (directly or indirectly) the creation of the task T2
>    begins finalization prior to either initiating
>    the activation of task T2 or returning task T2 to a
>    caller as part or coextension of a return object, task T2
>    is never activated and becomes terminated.

This looks fine to me (and yes, there is a danger, in fact certainty, of
confusion).

> But as indicated above, I think the Paris wording is fine, because
> of the use of the phraseology "master includes," which really means
> "the execution of the master construct includes."

This is news to me. If we meant "the execution of the master construct
includes", we should say so, because otherwise I would expect that what is
"included" in a master to be a statically determined thing (as opposed to
the nesting of masters, which is dynamic). Ada doesn't allow declarations to
occur dynamically!

> I don't think
> we have in the past implied that a task "includes" the execution of its
> subtasks.

But in that case, I have no idea what "indirect" means in this context. If a
master M1 includes the creation of a master M2, then it surely "indirectly"
includes the execution of its subtasks. How could it not? The master created
the task! Indeed, just about the only thing that *could* be indirectly
included would be a subtask, because everything else is directly included
(assuming that masters are transitive, which is something we've generally
assumed elsewhere).

My understanding was that the "(directly or indirectly)" was added to ensure
that transitivity was obvious. But it has exactly the opposite effect,
making it seem likely that unrelated stuff is included. I think an AARM note
explaining precisely what was meant would be more useful, because it can use
as many words as needed to be clear (a parenthetical remark cannot do that).

When it comes to accessibility and masters, *everything* is confusing! We
need to use wording that is absolutely crystal-clear and leaves as little to
the imagination as possible. And I don't want people to have to understand
every nuance of accessibility and masters before they can figure out how
tasks terminate in the usual cases.

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

From: Robert A. Duff
Date: Thursday, July 19, 2007  3:03 PM

> > I'm not convinced there is a problem, because I don't believe
> > the execution of a subtask is *included* in the execution of
> > the parent task.

I agree with Tucker about what "included" means (or should mean).
Perhaps the notion of "dynamically enclosing (of one execution by another)"
defined in 11.4(2) would help here?  The definition makes it clear that only
one task is involved.

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

From: Tucker Taft
Date: Thursday, July 19, 2007  3:25 PM

I don't disagree with your [Randy's - ED] point about being clear,
but it is important to macro-expand the term "master"
to "the execution of a master construct" every place
you see it.  A master is an *execution*.  That is
pretty well established in 7.6.1(3/2) in the definition
of the term.  Of course, informal discussion often
treats "master" as a category of syntactic constructs,
but that of course is wrong... ;-)

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

From: Robert I. Eachus
Date: Sunday, July 22, 2007  8:48 PM

>   If a task T1 creates a task T2, and if a master within task T1
>   that includes (directly or indirectly) the creation of the task T2
>   begins finalization prior to either initiating
>   the activation of task T2 or returning task T2 to a
>   caller as part or coextension of a return object, task T2
>   is never activated and becomes terminated.

Hmm.  I think the source of the confusion here is mentioning task T1.  
We all know that the conditions being discussed can only happen if  
there something above the master which becomes terminated.  But that has 
nothing to do with  the conditions addressed.  We have an 
uninterruptable last wishes situation:

If a master that includes (directly or indirectly) the creation of a 
task begins finalization prior to either initiating the activation of 
the task or returning the task to a caller as part or coextension of a 
return object, the task is never activated and becomes terminated.

So leaving the scope of the master has to change the status of the 
created task to terminated.  But editing it down like this makes it 
clear that it is either redundant or wrong!  Currently 9.1(4) says: "For 
tasks created by the evaluation of an allocator, the activations are 
initiated as the last step of evaluating the allocator, after completing 
any initialization for the object created by the allocator, and prior to 
returning the new access value."  This separation into two steps means 
that the case where you have an allocator that creates more than one 
task can result in one task becoming activated and immediately aborting 
the parent before it can finish activating all the tasks.  Worse, what 
happens when a created task creates another task as part of its 
activation? I could, but, won't, write a test.

If we say instead:

If a master that includes (directly or indirectly) the creation of a 
task begins finalization prior to either initiating  the activation of 
the task or returning the task to a caller as part or coextension of a 
return object, the task becomes terminated.

There are no orphans around, and the order of creation of tasks in an 
allocator is immaterial.  Does this allow, in the nested tasks case, for 
a situation where the new task has observable effects during elaboration 
of the task body, then fails, taking out the parent?  Sure.  But I don't 
think the intent is to change that.  As I see it, all that we really 
need to do is insure that tasks don't outlive their master.

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

From: Randy Brukardt
Date: Monday, July 23, 2007  11:34 PM

> >   If a task T1 creates a task T2, and if a master within task T1
> >   that includes (directly or indirectly) the creation of the task T2
> >   begins finalization prior to either initiating
> >   the activation of task T2 or returning task T2 to a
> >   caller as part or coextension of a return object, task T2
> >   is never activated and becomes terminated.
>
> Hmm.  I think the source of the confusion here is mentioning task T1.

No, that was Tucker's attempt to *fix* the confusion. The problem is whether
"includes indirectly" includes nested tasks. (We don't want it to.) Tucker
claims that it does not, but has no language wording to prove that; I claim
it is unclear and thus we need to fix it.

> We all know that the conditions being discussed can only happen if
> there something above the master which becomes terminated.  But that has
> nothing to do with  the conditions addressed.  We have an
> uninterruptable last wishes situation:
>
> If a master that includes (directly or indirectly) the creation of a
> task begins finalization prior to either initiating the activation of
> the task or returning the task to a caller as part or coextension of a
> return object, the task is never activated and becomes terminated.

This wording has exactly the same meaning as the other one, and it makes the
"includes indirectly" problem much worse, because in the previous wording,
it is clear that the finalization of a master of a task other than the one
that created the task is irrelevant; that is not clear here.

> So leaving the scope of the master has to change the status of the
> created task to terminated.  But editing it down like this makes it
> clear that it is either redundant or wrong!  Currently 9.1(4) says: "For
> tasks created by the evaluation of an allocator, the activations are
> initiated as the last step of evaluating the allocator, after completing
> any initialization for the object created by the allocator, and prior to
> returning the new access value."  This separation into two steps means
> that the case where you have an allocator that creates more than one
> task can result in one task becoming activated and immediately aborting
> the parent before it can finish activating all the tasks.

This is well-defined: aborting a task causes any masters it has to be
finalized. So it is clearly covered by the wording.

> Worse, what happens when a created task creates another task as part of
its
> activation? I could, but, won't, write a test.

What's worse about that? Again, we're only interested in the master of the
task that created the task; and that will always be finalized. The inner
task will be terminated before the outer one, and that is as it should be.

> If we say instead:
>
> If a master that includes (directly or indirectly) the creation of a
> task begins finalization prior to either initiating  the activation of
> the task or returning the task to a caller as part or coextension of a
> return object, the task becomes terminated.

All you've done here is eliminate some wording which makes it clear that a
task can't be both terminated and pending actuvation, Essentially, you've
introduced a race condition.

> There are no orphans around, and the order of creation of tasks in an
> allocator is immaterial.  Does this allow, in the nested tasks case, for
> a situation where the new task has observable effects during elaboration
> of the task body, then fails, taking out the parent?  Sure.  But I don't
> think the intent is to change that.  As I see it, all that we really
> need to do is insure that tasks don't outlive their master.

There are no orphans in any proposed wording (or the existing wording, for
that matter), since every created task belongs to a master, and all masters
are finalized. So there is no way for an orphan to be created. I think
you're making a problem where none existed.

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

From: Robert I. Eachus
Date: Tuesday, July 24, 2007  11:41 AM

>No, that was Tucker's attempt to *fix* the confusion. The problem is whether
>"includes indirectly" includes nested tasks. (We don't want it to.) Tucker
>claims that it does not, but has no language wording to prove that; I claim
>it is unclear and thus we need to fix it.

Ah, I was assuming that you wanted to include created tasks that have 
not yet been activated.  As I read the intent, tasks that are not yet 
activated are affected, and running tasks are only affected if their 
master is aborted.  (They become abnormal, then must terminate before 
the master is completes finalization.) That was the distinction I was 
trying to make.  In that case, you have to split the hairs I was 
splitting about when a task gets activated.  My point was that in the 
case of record designated by an access value containing more than one 
task with say library unit masters, it matters whether all are activated 
simultaneously, or if some but not all tasks can be activated before the 
abort.

>>If a master that includes (directly or indirectly) the creation of a
>>task begins finalization prior to either initiating the activation of
>>the task or returning the task to a caller as part or coextension of a
>>return object, the task is never activated and becomes terminated.
>
>This wording has exactly the same meaning as the other one, and it makes the
>"includes indirectly" problem much worse, because in the previous wording,
>it is clear that the finalization of a master of a task other than the one
>that created the task is irrelevant; that is not clear here.

Right, it is that never "activated and becomes terminated" that concerns 
me.  If the master of the tasks is left as part of the termination, no 
problem.  But if the master of the created tasks is not finalized along 
with the task creating the new tasks, the question of indivisibility vs. 
some order matters.

>>So leaving the scope of the master has to change the status of the
>>created task to terminated.  But editing it down like this makes it
>>clear that it is either redundant or wrong!  Currently 9.1(4) says: "For
>>tasks created by the evaluation of an allocator, the activations are
>>initiated as the last step of evaluating the allocator, after completing
>>any initialization for the object created by the allocator, and prior to
>>returning the new access value."  This separation into two steps means
>>that the case where you have an allocator that creates more than one
>>task can result in one task becoming activated and immediately aborting
>>the parent before it can finish activating all the tasks.
>
>This is well-defined: aborting a task causes any masters it has to be
>finalized. So it is clearly covered by the wording.

Yes, my wording here probably should have used Tuck's task 
designations.  Even then it is not clear.  Let me try again.  Task T1 
creates two tasks by means of an allocator.  Task T1 is the master, or 
includes the master of task T2.  Task T3 and possibly more. are also 
created by the allocator, but have other masters.  For simplicity assume 
library level masters.

>>Worse, what happens when a created task creates another task as part
>>of its activation? I could, but, won't, write a test.
>
>What's worse about that? Again, we're only interested in the master of the
>task that created the task; and that will always be finalized. The inner
>task will be terminated before the outer one, and that is as it should be.

Maybe I should write the example.  T1 creates T2 and is directly or 
indirectly its master.  Task T2 as part of its activation creates T5 and 
T6 library level masters. T6 raises Tasking_Error as part of, or 
immediately subsequent to its activation.  If there are no handlers 
intervening this will terminate T2 then T1.  But what about T5?  Can T5 
have completed activation before T6?  If so since it has a library level 
master, it stays alive and can have observable effects.  I've always 
assumed that 9.2 allowed such tasks to survive termination of their 
creator, and that some such tasks and not others might have been 
activated, if one of a set of task activations raises Tasking_Error in 
its creator. See AARM 9.2(5.a). Tasking_Error is only raised once, even 
if several tasks being activated together fail.  This is where I get the 
"some order" semantics.  You don't have to elaborate the 
declarative_parts of the various task_bodies sequentially, but if you 
don't you have to collect all the Tasking_Errors raised and pass on just 
one. See AARM 9.2(5.b).  Yes, these are very much not normative text, 
but I don't think it is wise to change the implied model.

>>If we say instead:
>>
>>If a master that includes (directly or indirectly) the creation of a
>>task begins finalization prior to either initiating the activation of
>>the task or returning the task to a caller as part or coextension of a
>>return object, the task becomes terminated.
>
>All you've done here is eliminate some wording which makes it clear that a
>task can't be both terminated and pending activation, Essentially, you've
>introduced a race condition.

No, I'm trying to preserve a race condition!  As far as any 
implemetation is concerned, they can choose not to have tasks in such as 
state.  A task can become terminated prior to or in the middle of 
activation.  In particular, if an implementation decides to allow 
parallel activation, it is possible for two tasks each to see the other 
as pending activation or terminated. The same thing can occur if the 
parent or master is aborted--if a task is being elaborated on one 
processor while another is causing it to become abnormal, the task whose 
task body is being elaborated can be in both states simultaneously.

 From an RM perspective, we have a group of tasks being activated 
simultaneously from the point of the task doing the activation, but not 
from other points of view.  In particular if a task creates other tasks 
by allocators during activation, they can look at their creator. (Or for 
that matter, their siblings, if an object contains several tasks.) They 
cannot see the creating task as both callable and terminated, but which 
they see in certain programs should be implementation dependent.

>>There are no orphans around, and the order of creation of tasks in an
>>allocator is immaterial.  Does this allow, in the nested tasks case, for
>>a situation where the new task has observable effects during elaboration
>>of the task body, then fails, taking out the parent?  Sure.  But I don't
>>think the intent is to change that.  As I see it, all that we really
>>need to do is insure that tasks don't outlive their master.
>
>There are no orphans in any proposed wording (or the existing wording, for
>that matter), since every created task belongs to a master, and all masters
>are finalized. So there is no way for an orphan to be created. I think
>you're making a problem where none existed.
 
I think we are using slightly different definitions of orphans, but it 
doesn't matter in that we agree that there are none.  I was thinking of 
tasks being activated when their creator, not master, is aborted. Either 
the task becomes activated before its creator is terminated, or it dies 
before the creator is left.  In either case, it is safe for the 
implementation to refer to data owned by the creator during activation.  
Whether implemetations do that or copy any necessary data to the new TCB 
before activation is an implementation detail.

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

From: Randy Brukardt
Date: Tuesday, July 24, 2007  1:39 PM

...
> Right, it is that never "activated and becomes terminated" that concerns
> me.

That's Ada 83 wording (9.3(4)) and thus it has always been part of the
model.

> If the master of the tasks is left as part of the termination, no
> problem.  But if the master of the created tasks is not finalized along
> with the task creating the new tasks, the question of indivisibility vs.
> some order matters.

Activation is indivisible in the sense that a task either has started
activation or it has not. It's not possible for it to be between those two
states. And, for the purposes of this rule, it is only the start of
activation that matters, not the completion of it.

...
> Maybe I should write the example.  T1 creates T2 and is directly or
> indirectly its master.  Task T2 as part of its activation creates T5 and
> T6 library level masters.

I assume via allocators, because otherwise the master is the same as it's
parent.

> T6 raises Tasking_Error as part of, or
> immediately subsequent to its activation.  If there are no handlers
> intervening this will terminate T2 then T1.  But what about T5?  Can T5
> have completed activation before T6?

Completed activation is not interesting for the purposes of this rule; it is
only the start that matters.

> If so since it has a library level
> master, it stays alive and can have observable effects.

Yes, of course.

> I've always
> assumed that 9.2 allowed such tasks to survive termination of their
> creator, and that some such tasks and not others might have been
> activated, if one of a set of task activations raises Tasking_Error in
> its creator. See AARM 9.2(5.a). Tasking_Error is only raised once, even
> if several tasks being activated together fail.  This is where I get the
> "some order" semantics.  You don't have to elaborate the
> declarative_parts of the various task_bodies sequentially, but if you
> don't you have to collect all the Tasking_Errors raised and pass on just
> one. See AARM 9.2(5.b).  Yes, these are very much not normative text,
> but I don't think it is wise to change the implied model.

Nothing about this rule changes this model. After all, it only applies when
the master of the tasks is finalized. At that point, it is not possible for
a task to be activated (that always happens within the master that owns the
task).

If task T5 has begun activation, then this rule does not apply to it (it
says "prior to initiating the activation" after all). The other (unchanged)
termination rules will apply to that task. OTOH, if T5 has not begun
activation, then it never can (because the activation point has to have been
aborted/left before the master has started finalization), and this rule
applies. And it says that the task can never be activated and becomes
terminated.

> >>If we say instead:
> >>
> >>If a master that includes (directly or indirectly) the creation of a
> >>task begins finalization prior to either initiating the activation of
> >>the task or returning the task to a caller as part or coextension of a
> >>return object, the task becomes terminated.
> >
> >All you've done here is eliminate some wording which makes it clear that a
> >task can't be both terminated and pending activation, Essentially, you've
> >introduced a race condition.
>
> No, I'm trying to preserve a race condition!  As far as any
> implemetation is concerned, they can choose not to have tasks in such as
> state.  A task can become terminated prior to or in the middle of
> activation.  In particular, if an implementation decides to allow
> parallel activation, it is possible for two tasks each to see the other
> as pending activation or terminated.

No, that isn't possible, because a task can only become terminated after it
has completed all finalization. That can't be "in the middle of activation";
the activation has to have finished (or failed) in order for that to happen.

> The same thing can occur if the
> parent or master is aborted--if a task is being elaborated on one
> processor while another is causing it to become abnormal, the task whose
> task body is being elaborated can be in both states simultaneously.

No, that would be a broken implementation of Ada. The terminated state is
exclusive of all other possible states of a task. You can get to it from
many other states, but it cannot be in any other state.

> From an RM perspective, we have a group of tasks being activated
> simultaneously from the point of the task doing the activation, but not
> from other points of view.  In particular if a task creates other tasks
> by allocators during activation, they can look at their creator. (Or for
> that matter, their siblings, if an object contains several tasks.) They
> cannot see the creating task as both callable and terminated, but which
> they see in certain programs should be implementation dependent.

Now you're saying that it can't be in two states at once. Of course, the
exact state you are in will depend on the runtime, the execution speed of
the processor, etc. But it can't be in more than one state.

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

From: Randy Brukardt
Date: Saturday, July 24, 2007  12:00 AM

Tucker Taft wrote:

>   If a task T1 creates a task T2, and if a master within task T1
>   that includes (directly or indirectly) the creation of the task T2
>   begins finalization prior to either initiating
>   the activation of task T2 or returning task T2 to a
>   caller as part or coextension of a return object, task T2
>   is never activated and becomes terminated.

This wording has been bothering me for several days based on something
Robert Eachus said,
and I've finally figured out what's the issue is.

I can't convince myself that it must be the case that the same master
encloses the creation and the activation point of a task (even in the
absence of a return statement). There are so many things that create masters
now that it is hard to reason about them, and it is trivial to add some to
declarations (via expressions or Initialize routines).

In any case, I don't think we really need to talk about the creation the
task; that's not very relevant as it is only the activation point that
matters. If we don't do that, then we can eliminate two issues with this
wording: the "indirectly" business, and any concern about which masters are
involved (or not involved).

The wording could be something like:

"If a master that directly encloses the point where the activation of a task
T would be initiated begins finalization before the activation of T is
initiated, T becomes terminated and is never activated."

This wording is a bit redundant (perhaps someone has a idea to improve it),
but it's simpler because we've gotten rid of "indirect"; there is no need to
worry about normal returns here because such a return just changes the
location of activation (and this wording *only* worries about that
location).

We do have one problem though; a return statement which is exited before the
return still might actually execute the activation point - but we don't want
its tasks activated - ever. So we need a special case for that. And we
probably need an AARM note. (Square brackets mean redundant in the wording
proposal below.)

"If a return statement is left such that the return object is not returned
to the caller, any task that was created as part or coextension of the
return object immediately becomes terminated and is never activated
[irregardless of where it would have been activated]. Otherwise, if a master
that directly encloses the point where the activation of a task T would be
initiated begins finalization before the activation of T is initiated, T
becomes terminated and is never activated.

   AARM Notes: The first case can happen with an exception being raised in a return
   statement, by an exit or goto from an extended_return_statement, or by a
   return statement being aborted. Any tasks created for the return object of
   such a return statement are never activated.

   The second case can only happen if the activation point of T is not reached
   due to an exception being raised or the task being aborted. Note that this is
   exclusive; if the master is being finalized, we're already past the activation
   point and since Ada doesn't have any restart semantics, we can never get there."

I think this is better, although the wording of the first sentence is
somewhat awkward (I'd rather not talk about task creation, but I can't think
of any other way to put it). And it's annoying to put the rare case first.
But there's no dependence on "indirect" masters; everything is defined in
terms of a single, specific master for each task. For that reason, we can't
get in trouble if the definition of masters is tweaked and we don't have to
worry about what master created a task.

Thoughts? Clever ways to simply this wording?

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

From: Robert I. Eachus
Date: Wednesday, August 1, 2007  8:14 AM

> This wording has been bothering me for several days based on something
> Robert Eachus said, and I've finally figured out what's the issue is.

I knew we were talking past each other.  I think I now know what you 
were focusing on.

> I can't convince myself that it must be the case that the same master
> encloses the creation and the activation point of a task (even in the
> absence of a return statement). There are so many things that create masters
> now that it is hard to reason about them, and it is trivial to add some to
> declarations (via expressions or Initialize routines). 

I'm convinced that all activations are enclosed in the master, or the 
master is at library level.  The problem is that you can have nested 
activations, and when that happens, specifying the master in a sentence 
is tough. From an implementation point of view, it is easy.  The master 
is deterimined at compile time, by the static nesting of the appropriate 
declaration. Of course, that declaration can be a task declaration, an 
object declaration, or an access type declaration..

> In any case, I don't think we really need to talk about the creation the
> task; that's not very relevant as it is only the activation point that
> matters. If we don't do that, then we can eliminate two issues with this
> wording: the "indirectly" business, and any concern about which 
> masters are
> involved (or not involved).
>
> The wording could be something like:
>
> "If a master that directly encloses the point where the activation of 
> a task
> T would be initiated begins finalization before the activation of T is
> initiated, T becomes terminated and is never activated."

Good.  This is what I was really trying to explain. I hope you mean what 
I was asking for though.  Activation is an indivisible operation from 
the point of view of the activating task:  A group of tasks all get 
initiated at a particular point in the execution.

However, that covers a multitude of execution during the elaboration of 
the newly created tasks.  IF the elaboration of one task causes the  
task enclosing it and several others to become completed, is it possible 
that some tasks were activated and others never activated?  This new 
wording seems to avoid discussing that case.

> We do have one problem though; a return statement which is exited before the
> return still might actually execute the activation point - but we don't want
> its tasks activated - ever. So we need a special case for that. And we
> probably need an AARM note. (Square brackets mean redundant in the wording
> proposal below.)
>
> "If a return statement is left such that the return object is not returned
> to the caller, any task that was created as part or coextension of the
> return object immediately becomes terminated and is never activated
> [irregardless of where it would have been activated]. Otherwise, if a master
> that directly encloses the point where the activation of a task T would be
> initiated begins finalization before the activation of T is initiated, T
> becomes terminated and is never activated.
>
I don't think you need the first part of this paragraph given the last 
sentence in (Ada 2007) 9.2(4/2):

"For tasks that are part or coextensions of a single object that is not 
a stand-alone object, activations are initiated after completing any 
initialization of the outermost object enclosing these tasks, prior to 
performing any other operation on the outermost object. In particular, 
for tasks that are part or coextensions of the object created by the 
evaluation of an allocator 
<http://www.adaic.org/standards/05rm/html/RM-4-8.html#S0129>, the 
activations are initiated as the last step of evaluating the allocator 
<http://www.adaic.org/standards/05rm/html/RM-4-8.html#S0129>, prior to 
returning the new access value. For tasks that are part or coextensions 
of an object that is the result of a function call, the activations are 
not initiated until after the function returns."

But the otherwise clause in Randy's proposal again avoids the problem 
case I was worrying about.  What happens if one of the created tasks 
raises an exception during its elaboration? It has already started 
activation, and it is possible that several other tasks have as well.  
The task raising the exception becomes a failed task, no problem there.  
But what about the other tasks which may be initiated at the same point 
in the containing task? My read has always been that some tasks may have 
(started and) completed elaboration, and such tasks will not wait for 
all other tasks in the group to complete activation before executing the 
task body.  They exist and are active.

Later when one of the tasks in the group fails, these tasks will become 
abnormal, then completed etc.  It is easy to create a task that takes 
say an hour to elaborate before failing.  A (legitimate) implementation 
might do sequential elaboration in that case.  But AFAIK, it is not 
required.

Programmers for today's supercomputers want parallel elaboration of 
tasks.  Forget the programming language used for a minute.  Distributing 
code to thousands of CPU cores is not easy, and the subject of lots of 
study.  We don't want to put artificial barriers in their way.  My 
understanding is that in Ada, the activation point is a synchronization 
point for the creator and all the created tasks, but the completion of 
activation is a one way syncronization point.  When all the created 
tasks are activated successfully the creating task can proceed.  This 
may not seem simple, but in a message passing environment it is about as 
simple as possible.  The master task sends out messages telling each 
processor where in the code to begin execution.  If that task gets back 
a failure message, it sends out termination messages to all the created 
tasks, otherwise when it gets back the right number of successes, it can 
continue execution.  The one to many messages create the most (wall 
clock) overhead, and you want to have just one.  If Ada tasks required 
an additional synchronization at the end of activation, that would 
double the number of messages required as part of program initiation.  
More important, it would add a second one to many message.

I wrote another post trying to make what I see more visible.  Microsoft 
ate it--I left it open overnight without saving a copy, you would have 
thought I would have learned that lesson by now. :-(   Anyway, I was 
still trying to get the explanation right:

When one or more tasks are activated, the activating task, or main 
program, sees activation of a set of tasks as an indivisible operation, 
which may complete successfully, or raise Tasking_Error.

The activated task however, each has its own view of time, or progress 
through the program code. Activation of a task at execution time may 
overlap with the activation of other tasks.  We had that fight decades 
ago.  And if no one remembers the problems caused by sequential 
activation, don't bother to dig out 1982 (I think) papers.  The real 
killer was that multiple simultaneous activations could occur in some 
cases anyway, and in any case sequential elaboration had its problems.
Eventually, it just seemed silly to make an array of task objects a 
special case.  (Or a different case from an array of access to task 
values. ;-)

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

From: Randy Brukardt
Date: Thursday, August 2, 2007  10:12 AM

...
> However, that covers a multitude of execution during the elaboration of
> the newly created tasks.  IF the elaboration of one task causes the
> task enclosing it and several others to become completed, is it possible
> that some tasks were activated and others never activated?  This new
> wording seems to avoid discussing that case.

That's purely intentional; I don't want this wording to specify something
that was left unspecified in Ada in the past. I certainly think that an
abort of the activator task could leave some of the activating tasks
unactivated, and the wording needs to cover that case without requiring it.

...

> I don't think you need the first part of this paragraph given the last
> sentence in (Ada 2007) 9.2(4/2):
>
> "For tasks that are part or coextensions of a single object that is not
> a stand-alone object, activations are initiated after completing any
> initialization of the outermost object enclosing these tasks, prior to
> performing any other operation on the outermost object. In particular,
> for tasks that are part or coextensions of the object created by the
> evaluation of an allocator
> <http://www.adaic.org/standards/05rm/html/RM-4-8.html#S0129>, the
> activations are initiated as the last step of evaluating the allocator
> <http://www.adaic.org/standards/05rm/html/RM-4-8.html#S0129>, prior to
> returning the new access value. For tasks that are part or coextensions
> of an object that is the result of a function call, the activations are
> not initiated until after the function returns."

This text defines where the activations task place. But in this specific
case (a return statement that is completed (by a transfer of control or an
abort) but that does *not* return to the caller), we *never* want to
activate the tasks, *even* if the activation point is later executed (as it
will be if the transfer of control remains inside of the function and goes
to a point that also executes a return statement).

That's because the second return statement will also create tasks in the
return object, and it is that second set that we want activated, not the
first.

> But the otherwise clause in Randy's proposal again avoids the problem
> case I was worrying about.

I surely hope so; there is no reason to specify anything, and leaving it
unspecified is just fine. What's the problem with that? It's been
unspecified since the beginning of (Ada) time.

In any case, the only thing this wording considers indivisible is the
*start* of activation of a task. Once the activation has started, this
wording does not apply. Period. And this is only relevant for the task
itself, what the activator is doing is mostly irrelevent (which is why I
prefer this wording to Tucker's). The only thing that matters is that the
activator cannot both be starting the activation of a task and finalizing
something at the same time; because finalization is exclusive of other
activities of a task (which necessarily includes activations). In
particular, an aborted task cannot finalize anything until it has stopped
any other activities -- but there is nothing new about that (that's why
"immediate abort" is so hard to define).

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

From: Robert I. Eachus
Date: Friday, August 3, 2007  7:05 PM

Totally agree.  I originally got torqued out of shape by wording that 
involved that implied that activation of a group of tasks was an 
indivisible act.  Of  course, it looks that way from the point of view 
of the task doing the activations. It isn't and hasn't been for the 
other tasks, and the wording may not have intended to imply that.

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


Questions? Ask the ACAA Technical Agent