Version 1.3 of ais/ai-00101.txt
!standard C.7.1 (03) 99-08-31 AI95-00101/05
!class binding interpretation 95-10-12
!status Corrigendum 2000 99-05-25
!status WG9 approved 95-06-14
!status ARG approved 11-0-0 (by letter ballot) 96-06-05
!status ARG approved (subject to letter ballot) 9-0-1 95-11-01
!status received 95-10-12
!qualifier Error
!subject Abort_Task has a parameter of mode 'in'.
!summary
Task_Identification.Abort_Task takes a parameter of mode 'in':
procedure Abort_Task (T : Task_Id);
!question
C.7.1(3) shows procedure Abort_Task taking a parameter of mode 'in out'.
Is this correct? (No.)
!recommendation
(See summary.)
!wording
(See summary.)
!discussion
Abort_Task does not modify its parameter, which is a Task_ID.
Therefore, its parameter should be of mode 'in'.
Furthermore, if the parameter is of mode 'in out', then one cannot pass
a function call or a constant. For example, Abort_Task(Current_Task)
should be allowed. For another example, the following ought to be
allowed:
type Task_ID_Array is array (Natural range <>) of Task_ID;
procedure Abort_Some_Tasks(Tasks: Task_ID_Array) is
begin
for I in Tasks'range loop
Abort_Task(Tasks(I));
end loop;
end Abort_Some_Tasks;
...
Abort_Some_Tasks((This_Task_ID, That_Task_ID, The_Other_Task_ID));
Hence, this parameter should be of mode 'in'.
Note that Abort_Task is not analogous to Unchecked_Deallocation. After
a call to an instance of Unchecked_Deallocation, the designated object
ceases to exist, and any reference to it would be erroneous; therefore
it makes sense for Unchecked_Deallocation to set the access object to
null. However, after a call to Abort_Task, the task object continues to
exist, and the task might even keep running for a while. Therefore, it
does not make sense for Abort_Task to set its parameter to Null_Task_ID.
Note that it is harmless to abort the same task twice -- either with an
abort_statement, or with Abort_Task.
!corrigendum C.07.01(03)
Replace:
procedure Abort_Task (T : in out Task_Id);
by:
procedure Abort_Task (T : Task_Id);
!ACATS test
A C-Test is needed to check that calls to Abort_Task work even for functions
or constants as parameters.
!appendix
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!from Norman Cohen 95-09-06
!reference as: 95-5325.a Robert A Duff 95-10-11>>
!discussion
Ada.Task_Identification.Abort_Task takes a single parameter, of type
Task_ID. The parameter is of mode in out rather than in, even though
the semantics of Abort_Task given in C.7.1(9) do not indicate that the
procedure alters its parameter.
As Keith Thompson pointed out to me, this makes the call
Abort_Task(Current_Task) illegal, so the in out mode does no good and a
small amount of harm. It ought to be changed to mode in by a binding
interpretation. (If this were still the language-design stage, I would
also suggest giving the parameter the default-value expression
Current_Task.)
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference Norman Cohen 95-09-06
!reference 95-5325.a Robert A Duff 95-10-11
!from Offer pazy 95-10-12
!reference as: 95-5331.a Offer Pazy 95-10-12>>
!discussion
Norm cohen writes:
> Ada.Task_Identification.Abort_Task takes a single parameter, of type
> Task_ID. The parameter is of mode in out rather than in, even though
> the semantics of Abort_Task given in C.7.1(9) do not indicate that the
> procedure alters its parameter.
>
> As Keith Thompson pointed out to me, this makes the call
> Abort_Task(Current_Task) illegal, so the in out mode does no good and a
> small amount of harm. It ought to be changed to mode in by a binding
> interpretation.
> (If this were still the language-design stage, I would
> also suggest giving the parameter the default-value expression
> Current_Task.)
This is clearly a bug, but I'd rather have the semantics of the parameter
upon return changed (to its original, mistakenly dropped, intent) as opposed
to making it an IN only parameter. The parameter should be Null_Task_Id
upon return to prevent the object from being used again for other operations
requiring Task_Id. This is similar to other destroy/free operations and
seems to be the desired semantics.
It is true that in this way, one cannot call it directly for the result of
Current_Task, but I don't think that this is often the need (I definitely
would not recommed having teh default be Current_Task, a bug here will get
totally undetected which would be unfriendly. Aborting a task should not be
"easy", you should be required to specify which task you want to abort, even
if it costs you typing several more characters :-). When aborting self is
needed, one can use a local variable to store the Task_Id value od self.
Offer Pazy
31 Robinwood Ave.
Boston, MA 02130
USA
(617)522-5988
pazy@world.std.com
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference 95-5325.a Robert A Duff 95-10-11
!from Ted Baker 95-09-12
!reference as: 95-5332.a Ted Baker 95-10-12>>
!discussion
I can't think of any good reason for the task ID parameter of
Abort_Task to be "in out". In fact, I never noticed it. I
suspect this was a typo, introduced somewhere along the way.
It should be scheduled to be changed, in the first set of
technical corrigenda. Such a change should be upward-compatible
from a user's point of view.
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference Norman Cohen 95-09-06
!reference 95-5325.a Robert A Duff 95-10-11
!reference 95-5331.a Offer Pazy 95-10-12
!from Ted Baker 95-10-13
!reference 95-5339.a Ted Baker 95-10-13>>
!discussion
Offer Pazy writes:
| ... I'd rather have the semantics of the parameter
| upon return changed (to its original, mistakenly dropped, intent) as opposed
| to making it an IN only parameter. The parameter should be Null_Task_Id
| upon return to prevent the object from being used again for other operations
| requiring Task_Id. This is similar to other destroy/free operations and
| seems to be the desired semantics.
The situation is not exactly analogous to unchecked deallocation.
The task may continue to execute, and then to exist, for some time
after it is aborted, so we may still meaningfully reference the
corresponding ID.
--Ted
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference Norman Cohen 95-09-06
!reference 95-5325.a Robert A Duff 95-10-11
!reference 95-5331.a Offer Pazy 95-10-12
!reference 95-5339.a Ted Baker 95-10-13
!from Robert I. Eachus 95-10-16
!reference 95-5343.a Robert I. Eachus 95-10-16>>
!discussion
Ted Baker said:
> The situation is not exactly analogous to unchecked deallocation.
> The task may continue to execute, and then to exist, for some time
> after it is aborted, so we may still meaningfully reference the
> corresponding ID.
Really?
RM 9.8(1): "An abort_statement causes one or more tasks to become
abnormal, thus preventing any further interaction with such tasks."
RM95 C.7.1(9): "The effect of Abort_Task is the same as the
abort_statement for the task identified by T."
RM95 C.7.1(18): "If a value of Task_ID is passed as a parameter to
any of the operations declared in this package (or any language
defined child of this package), and the corresponding task object no
longer exists, the execution of the program is erroneous."
(Hmmm... This reads a lot harsher than intended. My point is that
any attempt to reference a task after it is aborted is either futile
or fraught with danger. A program designed knowing the specifics of
the implementation may be able to do something meaningful, but it is
extremely implementation dependent.)
Robert I. Eachus
with Standard_Disclaimer;
use Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference Norman Cohen 95-09-06
!reference 95-5325.a Robert A Duff 95-10-11
!reference 95-5331.a Offer Pazy 95-10-12
!reference 95-5339.a Ted Baker 95-10-13
!from Ted Baker 95-10-16
!reference 95-5344.a Ted Baker 95-10-16>>
!discussion
I said:
> The situation is not exactly analogous to unchecked deallocation.
> The task may continue to execute, and then to exist, for some time
> after it is aborted, so we may still meaningfully reference the
> corresponding ID.
| Robert Eachus asked:
| Really?
Yes, REALLY.
| RM 9.8(1): "An abort_statement causes one or more tasks to become
| abnormal, thus preventing any further interaction with such tasks."
That text is part of the introduction, carried over almost
unchanged from Ada'83. It was probably an editorial mistake to
replace to simply replace "rendezvous" by "interaction", which is
less well defined. However, in any case, there are meaningful
ways to refer to a task via it's ID that do not involve
interaction.
| RM95 C.7.1(9): "The effect of Abort_Task is the same as the
| abort_statement for the task identified by T."
Fine.
| RM95 C.7.1(18): "If a value of Task_ID is passed as a parameter to
| any of the operations declared in this package (or any language
| defined child of this package), and the corresponding task object no
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| longer exists, the execution of the program is erroneous."
^^^^^^^^^^^^^
Note that the task object may continue to exist (and the task to
execute) after the task is aborted, for an abitrarily long time.
Moreover, it is possible to write programs in a way that
guarantees the the task will continue to exist.
| (Hmmm... This reads a lot harsher than intended. My point is that
| any attempt to reference a task after it is aborted is either futile
| or fraught with danger. A program designed knowing the specifics of
| the implementation may be able to do something meaningful, but it is
| extremely implementation dependent.)
No. It does not rely on anything special about the implementation.
In fact, the situation is not much different than in Ada'83, since
we already had the 'Terminated attribute then, which could
meaningfully be called to check up on how an aborted task is
coming along. In Ada'95 we also have Is_Terminated.
Moreover, if we know the task has not yet terminated, we can use
values of task-specific attributes.
--Ted Baker
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference 95-5325.a Robert A Duff 95-10-11
!reference 95-5331.a Offer Pazy 95-10-12
!reference 95-5339.a Ted Baker 95-10-13
!reference 95-5343.a Robert I. Eachus 95-10-16
!reference 95-5344.a Ted Baker 95-10-16
!reference 95-5345.a Robert I. Eachus 95-10-16>>
!discussion
(Let me say it up front this time. The furious sound of the debate
should not be allowed to conceal the fact that Ted's position is very
close to mine. But in this forum, we can't blithely decide to accept
some parts of the RM and not others. If it is broken, we need to fix
it. Personally I don't think the RM is broken, but the ice is awful
thin.)
| RM 9.8(1): "An abort_statement causes one or more tasks to become
| abnormal, thus preventing any further interaction with such tasks."
Ted said:
> That text is part of the introduction, carried over almost
> unchanged from Ada'83. It was probably an editorial mistake to
> replace to simply replace "rendezvous" by "interaction", which is
> less well defined. However, in any case, there are meaningful
> ways to refer to a task via it's ID that do not involve
> interaction.
Either, it's right, it's a note, or we need a binding
interpretation. I think it is correct. Any action which requires
interaction--some action as well by the aborted task--is no longer
possible. And I agree, that there are some meaningful inquiries which
do not require interaction.
| RM95 C.7.1(9): "The effect of Abort_Task is the same as the
| abort_statement for the task identified by T."
> Fine.
Agreed. ;-)
| RM95 C.7.1(18): "If a value of Task_ID is passed as a parameter to
| any of the operations declared in this package (or any language
| defined child of this package), and the corresponding task object no
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| longer exists, the execution of the program is erroneous."
^^^^^^^^^^^^^
> Note that the task object may continue to exist (and the task to
> execute) after the task is aborted, for an abitrarily long time.
Agreed.
> Moreover, it is possible to write programs in a way that
> guarantees the the task will continue to exist.
Definitely not portably. (Hmmm. Not quite true. You can abort
the task that call you inside a rendezvous, and continue to refer to
that task until the rendezvous is left. Is there a meaningful use for
this?)
> No. It does not rely on anything special about the implementation.
Yes, it does. It requires an implementation where the name of an
aborted task remains valid after the abort. You may not be able to
conceive of such an implementation, but I can, and the cost of keeping
the name alive would be high. (Basically an implementation where
tasks have their own address spaces, and accesses are provided by
capabilities. Since the a tasks may be on different processors, there
should be no requirement to wait before flushing the capability for an
aborted task.)
> In fact, the situation is not much different than in Ada'83, since
> we already had the 'Terminated attribute then, which could
> meaningfully be called to check up on how an aborted task is
> coming along. In Ada'95 we also have Is_Terminated.
You make my point better than you could imagine. Requiring
'TERMINATED to work outside the tasks master was one of the biggest
mistakes the ARG made. (Corrected later, but it did a lot of damage.)
Let's not repeat history.
> Moreover, if we know the task has not yet terminated, we can use
> values of task-specific attributes.
Didn't I say risky? Since the task may terminate at any point
where it is not in an abort protected region. (And any aborted task
cannot BE in an abort protected region--think about it.) So the task
can and will go away under you--unless you know a lot of
implementation details.
Robert I. Eachus
with Standard_Disclaimer;
use Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference 95-5325.a Robert A Duff 95-10-11
!reference 95-5331.a Offer Pazy 95-10-12
!reference 95-5339.a Ted Baker 95-10-13
!reference 95-5343.a Robert I. Eachus 95-10-16
!reference 95-5344.a Ted Baker 95-10-16
!from Ted Baker 95-10-16
!reference 95-5346.a Ted Baker 95-10-16>>
!discussion
It seems we may be wasting words over some confusion between
the meanings of abort, terminate, and exist.
1) Task abortion does not imply immediate termination.
2) Task termination does not imply the task necessarily is allowed to
immediately cease to exist, or that no further reference to it is allowed.
| RM 9.8(1): "An abort_statement causes one or more tasks to become
| abnormal, thus preventing any further interaction with such tasks."
I don't believe the term "interaction" is formally defined.
Clearly, rendezvous and requeuing on entries of the task are ruled
out. I don't think it is worth pushing the envelope further at
this time, since it does not affect the outcome present question.
I said it is possible to write programs in a way that guarantees a
task will continue to exist up to some defined point after it is
aborted.
Robert said (but then withdrew the statement on the same line):
| Definitely not portably. (Hmmm. Not quite true. You can abort
| the task that call you inside a rendezvous, and continue to refer to
| that task until the rendezvous is left. Is there a meaningful use for
| this?)
Yes, there are several ways of doing this portably. Abort
deferral during protected actions and during finalization provide
plenty of opportunity.
I said that this does not rely on anything implementation-dependent.
Robert said:
| Yes, it does. It requires an implementation where the name of an
| aborted task remains valid after the abort. You may not be able to
| conceive of such an implementation, but I can, and the cost of keeping
| the name alive would be high. (Basically an implementation where
| tasks have their own address spaces, and accesses are provided by
| capabilities. Since the a tasks may be on different processors, there
| should be no requirement to wait before flushing the capability for an
| aborted task.)
All this is required of the implementation by the Ada standard.
Reference to the task ID value remains valid until the task no
longer exists. It must continue to exist until either the scope
of the task object is left, or the task object is
unchecked-deallocated.
Robert said:
| ... requiring 'TERMINATED to work outside the tasks master was one
| of the biggest mistakes the ARG made. (Corrected later, but it
| did a lot of damage.) Let's not repeat history.
Inside or outside the task's master is not an issue here. A task
may be meaningfully referenced, either by name or via task ID
value, within its own master, after the task has been aborted.
--Ted Baker
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference 95-5325.a Robert A Duff 95-10-11
!reference 95-5331.a Offer Pazy 95-10-12
!reference 95-5339.a Ted Baker 95-10-13
!reference 95-5343.a Robert I. Eachus 95-10-16
!reference 95-5344.a Ted Baker 95-10-16
!from Offer Pazy 95-10-16
!reference 95-5347.a Offer Pazy 95-10-16>>
!discussion
I still like the idea of Abort_Task returning Null_Task_Id, but the
arguments presented by Ted are completely correct, so maybe the right
solution is changing the formal to "in". There are many (legitimate and
intentional) way to interact with an abnormal task (as Ted pointed out).
The "interaction" word at the introduction is totally non-normative. Of
course you can, even fo rrendezvous, but then you get Tasking_error. It
does not mean that the task does not exist or that the implementation is
allowed to not support this. The name (and the task-id) of the task exist
until it goes out of scope or unchecked deallocated (even in Ada 83 you
could do 'Terminated or 'callable on such tasks. In fact, in several places
in the annexes, we have made special arrangements to allow user to refer
*safely* to abnormal but not yet terminated tasks (attribute, dynamic
priorities!, Hold, etc.), so let's not confuse the issues, there is nothing
impl-defined here. And it has nothing to do with abort deferred regions
either, there is a time period between when the task becomes abnormal and
when it is terminated. This window may be visible on multi processors, and
the semantics make it sage to refer to such a task, since tasking error will
be raised, if you did it a bit too late.
So there is no problem here and let's go back to the original issue. I have
stated my prefernces, but I can see the other points too, so I don't feel
strongly either way anymore. But please, please, please (Bevakasha Norm),
let's not have the default be Curernt_Task. It is very strange that the
default if you do something wrong would be a suicide (also difficult to
debug). It is true that more and more state adopt the capital punishment,
but even there it applies only if you succeed, not if you "fail".
Offer Pazy
31 Robinwood Ave.
Boston, MA 02130
USA
(617)522-5988
pazy@world.std.com
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!from Robert I. Eachus 95-10-17
!reference 95-5348.a Robert I. Eachus 95-10-17>>
!discussion
I figured it was time to separate this topic from the discussion on
Abort_Task, becuase it seems to me as if a binding interpretation is
likely to be required, whatever the "correct" interpretation is. I
think the correct answer is that there are some--but very few--
non-erroneous cases where T'Terminated, T'Callable, Is_Terminated, and
Is_Callable can be used with an aborted task. However 13.9.1(8) seems
very draconian:
RM 13.9.1(8): "It is erroneous to evaluate a primary that is a name
denoting an abnormal object, or to evaluate a prefix that denotes an
abnormal object."
I think it was only intended to apply to names involved in
disrupted assignments, but 13.9.1(5) specifically mentions an abort as
a way for an object to become abnormal, and 9.8(1) is much more
explicit:
RM 9.8(4): "Each named task is then aborted, which consists of
making the task abnormal and aborting the execution of the
corresponding task_body unless it is already completed."
In addition we have:
RM 9.8(1): "An abort_statement causes one or more tasks to become
abnormal, thus preventing any further interaction with such tasks."
RM95 C.7.1(9): "The effect of Abort_Task is the same as the
abort_statement for the task identified by T."
RM95 C.7.1(18): "If a value of Task_ID is passed as a parameter to
any of the operations declared in this package (or any language
defined child of this package), and the corresponding task object no
longer exists, the execution of the program is erroneous."
It seems clear that there are many cases where T'Terminated or
Is_Terminated for an aborted task must be erroneous, and I see little
advantage to users to resolving many of them in any other way. (For
example, if you pass a Task_ID outside the scope of it's master, which
is the case C.7.1(18) addresses.) Are there cases where we should
guarantee the validity? Section 9.9 (2&3) implies that there are.
The definition of T'Callable is that "...a task is callable unless it
is completed or abnormal," which certainly implies that there are some
cases where it is non-erroneous for an abnormal task. The only one I
could think of is within the aborted task. If the aborted task is the
called task in a rendezvous, this might even be meaningful.
So I can see a faint possibility of resolving this as a
ramification, but not really. We would be saying that the name of a
task is never abnormal within the task itself, but the attribute
'Callable depends on the outside view.
Finally are there other attributes which are valid for otherwise
erroneous prefixes? For example if an array assignment is disrupted,
is there any reason to consider A'FIRST erroneous? Probably not worth
worrying about, but someone else may want to broaden this issue.
Robert I. Eachus
with Standard_Disclaimer;
use Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!reference 95-5348.a Robert I. Eachus 95-10-17
!from Tucker Taft 95-10-18
!reference 95-5349.a Tucker Taft 95-10-18>>
!discussion
> I figured it was time to separate this topic from the discussion on
> Abort_Task, becuase it seems to me as if a binding interpretation is
> likely to be required, whatever the "correct" interpretation is. I
> think the correct answer is that there are some--but very few--
> non-erroneous cases where T'Terminated, T'Callable, Is_Terminated, and
> Is_Callable can be used with an aborted task. However 13.9.1(8) seems
> very draconian:
>
> RM 13.9.1(8): "It is erroneous to evaluate a primary that is a name
> denoting an abnormal object, or to evaluate a prefix that denotes an
> abnormal object."
>
> I think it was only intended to apply to names involved in
> disrupted assignments, but 13.9.1(5) specifically mentions an abort as
> a way for an object to become abnormal, and 9.8(1) is much more
> explicit:
>
> RM 9.8(4): "Each named task is then aborted, which consists of
> making the task abnormal and aborting the execution of the
> corresponding task_body unless it is already completed."
This confusion is due to an unfortunate overloading of the term
"abnormal." An abnormal task is not an abnormal object!
I never noticed this overloading until just now, since the two
meanings were always completely separated in my mind.
Now I see that using "abnormal" for objects resulting from a disrupted
assignment was a very unfortunate choice of terms. This use of
"abnormal" has nothing whatsoever to do with the state of a task
called "abnormal."
Probably at this point, the simpler "fix" is to change all uses
of the term "abnormal" relating to a task to be simply "uncallable."
This is really the only guaranteed immediate affect of aborting
a task -- rendering the task uncallable.
Aborting a task has no effect on the task object, and certainly doesn't
make the task object become nonexistent. That only happens due
to unchecked_deallocation or leaving the scope where the task object
is declared. You can test 'Callable and 'Terminated completely
safely after aborting a task; you can refer to the discriminants of the
task object; you can ask the 'Size or the 'Address of the task object.
As A. A. Milne would say, there is not even a smidgen of erroneousness
about it...
... [the rest is elided due to this fundamental confusion...]
> Robert I. Eachus
-Tuck
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!reference 95-5348.a Robert I. Eachus 95-10-17
!reference 95-5349.a Tucker Taft 95-10-18
!from Robert I. Eachus
!reference 95-5350.a Robert I. Eachus 95-10-18>>
!discussion
Tucker said:
> This confusion is due to an unfortunate overloading of the term
> "abnormal." An abnormal task is not an abnormal object!
> I never noticed this overloading until just now, since the two
> meanings were always completely separated in my mind.
A lot of us didn't, that's how these things slip through. But as I
said, I think we can't clean up this one without a binding interpretation.
> Now I see that using "abnormal" for objects resulting from a disrupted
> assignment was a very unfortunate choice of terms. This use of
> "abnormal" has nothing whatsoever to do with the state of a task
> called "abnormal."
I wish it were that easy to bell the cat. It is clear that
aborting a task can make tasks objects abnormal in the 13.9.1 sense.
The right cure is probably to keep both uses of abnormal, and
distinguish between an abnormal task object and an abnormal task.
> Probably at this point, the simpler "fix" is to change all uses
> of the term "abnormal" relating to a task to be simply "uncallable."
> This is really the only guaranteed immediate affect of aborting
> a task -- rendering the task uncallable.
No. Aborting a task can interrupt an assignment which is not
abort deferred, and it will render all tasks for which the aborted
task is the master abnormal in the 13.9.1 sense--but not always
immediately. This is what makes fixing the problem a testy chore.
We need to keep the sentence in 13.9.1(5), and fix 9.8.
Robert I. Eachus
with Standard_Disclaimer;
use Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!reference 95-5348.a Robert I. Eachus 95-10-17
!from Bob Duff
!reference 95-5351.a Robert A Duff 95-10-18>>
!discussion
I think it's time to clarify some terminology.
The RM uses the term "abnormal" in two different ways. Now, with
hindsight, that seems like maybe not such a good idea. ;-)
In 13.9.1, "abnormal" is applied to objects. It is, indeed, erroneous
to do just about anything to an abnormal object.
In 9.8, "abnormal" is applied to tasks. Referencing an abnormal task is
*not* erroneous. In particular, T'Terminated, where T denotes a task
object containing an abnormal task, is *not* erroneous. Similarly,
calling entries of an abnormal task is *not* erroneous.
There also seems to be some confusion about when a task object ceases to
exist. In C.7.1, we find that it's erroneous to use a Task_ID in
certain ways, if the task *object* corresponding to that Task_ID no
longer exists. Aborting a task does *not* make any objects stop
existing. An object ceases to exist when you leave the master in which
the object is declared. Heap objects cease to exist when
Unchecked_Deallocation is done. This is true of all objects, and task
objects are no exception. Thus, calling Abort_Task(ID), where ID
identifies a task that has been aborted already, is *not* usually
erroneous. It's only erroneous if the ID has escaped outside the task's
master.
Consider three tasks T1, T2, and T3. If T2 and T3 both do:
abort T1;
simultaneously, then one of them will grab T1 first, and make this task
abnormal. The second abort of T1 is harmless -- NOT erroneous.
The same is true for:
Abort_Task(T1_Id);
where T1_Id = T1'Identity.
All of the above follows directly from the RM, and I see no need for any
AI to address these issues. (Although I admit that if we were still in
RM-writing mode, I would reconsider the idea of using "abnormal" to mean
two completely different things.)
Back to the original question, which is whether Abort_Task should take
an 'in' or 'in out' parameter: Offer suggested that aborting a task is
analogous to Unchecked_Deallocation, which takes in 'in out' parameter,
so it can null it out. However, I claim these two things are not
analogous. After U_D, the designated object ceases to exist, and
deallocating the same object twice is erroneous. But aborting a task
does not cause the task *object* to stop existing. And aborting the
same task twice is harmless. Therefore, I do not think it's a good idea
to null out the task ID.
Furthermore, Abort_Task is supposed to behave just like an
abort_statement (except that one takes a Task_ID, whereas the other
takes a task object). An abort_statement does not null out anything;
therefore, Abort_Task should not either.
Thus, Abort_Task does not (and should not) modify its parameter in any
way. Therefore, this parameter should be of mode 'in'.
As pointed out originally by Norm Cohen, making the parameter be of mode
'in' has the advantage that you can say Abort_Task(Current_Task) without
declaring an extra temp variable. The same thing is true of a
user-defined function that returns a Task_ID -- it would be convenient
to allow a call to such a function to be passed to Abort_Task. Or a
constant -- consider a procedure Abort_Several_Tasks, which takes a
parameter of mode 'in', which is an array of several Task_IDs.
Abort_Several_Tasks should simply loop through the array, calling
Abort_Task -- it should not have to declare an extra temp variable.
Finally, Norm Cohen also suggested that it might make sense to make
Current_Task be the default for Abort_Task. I agree with Offer that we
should not do that. Suicide should not be the default behavior.
- Bob
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!from Robert I. Eachus 95-10-17
!reference 95-5348.a Robert I. Eachus 95-10-17
!from Offer pazy 95-10-18
!reference 95-5352.a Offer Pazy 95-10-18>>
!discussion
There is nothing ic common (I hope) between abnormal tasks and abnormal
objects. If we did not screw up in CH 9 C and D, the discussion is always
about *abnormal tasks* and not *abnormal task objects* and this is the
difference that make you analysis inapplicable (again, I hope).
If you could assign task objects and then the assignment would abort, then
the task object would become abnormal, not the task itself. So personally, I
don't see and need for binding interpretation or any other clarification.
To emphasize, it is perfectly OK (and meaningful) to use the attributes and
the operations in Task_Identificatio on abnormal tasks.
Offer Pazy
31 Robinwood Ave.
Boston, MA 02130
USA
(617)522-5988
pazy@world.std.com
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!reference 95-5348.a Robert I. Eachus 95-10-17
!reference 95-5349.a Tucker Taft 95-10-18
!reference 95-5350.a Robert I. Eachus 95-10-18
!from Bob Duff
!reference 95-5353.a Robert A Duff 95-10-19>>
!discussion
>...But as I
> said, I think we can't clean up this one without a binding interpretation.
...
> I wish it were that easy to bell the cat. It is clear that
> aborting a task can make tasks objects abnormal in the 13.9.1 sense.
Robert,
I think you are missing the fact that 13.9.1 defines "abnormal object",
whereas 9.8 defines "abnormal task". A task is not an object. A task
object is not a task, but *contains* a task. Therefore, it is clear
that aborting a task DOES NOT make task objects abnormal (in the 13.9.1
sense). Aborting a task makes *tasks* abnormal, which has nothing to do
with 13.9.1.
I again apologize for overloading the term "abnormal". But this
overloading is quite correct, and is resolved by whether you're talking
about a task or an object.
- Bob
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!reference 95-5348.a Robert I. Eachus 95-10-17
!reference 95-5352.a Offer Pazy 95-10-18
!from Robert I. Eachus 95-10-18
!reference 95-5355.a Robert I. Eachus 95-10-19>>
!discussion
First, after rereading RM95 9.8 and comparing it with RM83 9.10, the
newer wording avoids the use of abnormal after the defining instance.
In fact the only further references to abnormal in the chapter seem to
be in 9.8(21) which uses it in the 13.9.1 sense, and specifically
references 13.9.1, and in 9.9(2), the definition of callable.
But the use of abnormal in 9.9(2) refers to the "Task denoted by T."
There is no way I can construct this as refering other than to the
task object without explicit wording distinguishing the task object
denoted by the name from the task created by the elaboration of the
object declaration. RM 9.2(2) has a chance to do that and doesn't:
"A task object (which represents one task) can be created either as
part of the elaboration of an object_declaration, or as part of the
evaluation of an allocator. All TASKS created by the elaboration
of..."
So I think it takes a BI to distinguish between task objects and the
tasks themselves. In particular look at 13.11.2(9): "...if the object
being freed contains tasks, the object might not be deallocated."
Offer Pazy said:
> There is nothing ic common (I hope) between abnormal tasks and abnormal
> objects. If we did not screw up in CH 9 C and D, the discussion is always
> about *abnormal tasks* and not *abnormal task objects* and this is the
> difference that make you analysis inapplicable (again, I hope).
I agree that we need to distinguish between tasks in an abnormal
state, and task objects which are abnormal. If I thought that making
that distinction would be enough, we could agree to it and go on to
other issues. But there is another problem. When
you follow the 9.8(5) definition of aborted to 13.9.1(5), you find
that aborts can and do make objects erroneous (and it must in some
cases). Now the INTENT surely was that an assignment in an aborted
task not make that entire task object abnormal, and 13.9.1(7)
certainly allows implementations not to do that. But the opposite
choice is also implied. The clarifaction really needed here is that
13.9.1(4) discusses objects or parts of objects becoming abnormal, but
doesn't say when assignment to a subcomponent is allowed to make the
entire object abnormal.
> If you could assign task objects and then the assignment would
> abort, then the task object would become abnormal, not the task
> itself. So personally, I don't see and need for binding
> interpretation or any other clarification.
If you expect the "average" reader of the RM, who by the way is
pretty sophisticated, to not only pick up on this subtlety, but manage
to correctly decide which tasks become abnormal in each sense, then
there is no need for an AI. But I can't find the distinction in the
RM, so I think that an AI is required. I also think that it should be
written as a binding interpretation.
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!reference 95-5348.a Robert I. Eachus 95-10-17
!reference 95-5351.a Robert A Duff 95-10-18
!from Robert I. Eachus 95-10-19
!reference 95-5356.a Robert I. Eachus 95-10-19>>
!discussion
> I think it's time to clarify some terminology.
So do I. ;-)
> The RM uses the term "abnormal" in two different ways. Now, with
> hindsight, that seems like maybe not such a good idea. ;-)
Agreed.
> In 9.8, "abnormal" is applied to tasks. Referencing an abnormal task is
> *not* erroneous. In particular, T'Terminated, where T denotes a task
> object containing an abnormal task, is *not* erroneous. Similarly,
> calling entries of an abnormal task is *not* erroneous.
Ah, this seems to be the intent. But quote me the words which say
that tasks are not task objects. In my reply to Offer, I quoted
evidence that there is no distinction made between a task and task
object in the RM. In fact, the term task object is only used to avoid
confusion with task types.
> There also seems to be some confusion about when a task object
> ceases to exist. In C.7.1, we find that it's erroneous to use a
> Task_ID in certain ways, if the task *object* corresponding to
> that Task_ID no longer exists. Aborting a task does *not* make
> any objects stop existing. An object ceases to exist when you
> leave the master in which the object is declared. Heap objects
> cease to exist when Unchecked_Deallocation is done. This is true
> of all objects, and task objects are no exception. Thus, calling
> Abort_Task(ID), where ID identifies a task that has been aborted
> already, is *not* usually erroneous. It's only erroneous if the
> ID has escaped outside the task's master.
I agree with all of this except the last sentence. If a task object
still exists, I agree that 'TERMINATED should work. That is why I
wrote the comment. But there are three ways for a task object to
disappear: 1) If the scope of the master is left. 2) If the task
object, or an object containing the task (13.11.2). 3) If no reference
to the task object exists, and the task becomes completed.
You may argue that garbage collection for tasks which are referenced
by Task_IDs are an exception to the last case. I personally think
that Task_IDs can be ignored by garbage collectors, but that it would
be very unfriendly to do so. However, you must interpret the RM to
allow garbage collection for tasks which are not the targets of
Task_IDs. General garbage collection in Ada is tough, but recycling
tasks should be easy.
> Consider three tasks T1, T2, and T3. If T2 and T3 both do... All
> of the above follows directly from the RM, and I see no need for
> any AI to address these issues. (Although I admit that if we were
> still in RM-writing mode, I would reconsider the idea of using
> "abnormal" to mean two completely different things.)
No argument (unless one of the task is the master of another).
> Back to the original question, which is whether Abort_Task should take
> an 'in' or 'in out' parameter: Offer suggested that aborting a task is
> analogous to Unchecked_Deallocation, which takes in 'in out' parameter,
> so it can null it out. However, I claim these two things are not
> analogous. After U_D, the designated object ceases to exist, and
> deallocating the same object twice is erroneous. But aborting a task
> does not cause the task *object* to stop existing. And aborting the
> same task twice is harmless. Therefore, I do not think it's a good idea
> to null out the task ID.
We need to argue this in the original chain. However, discussion
of the assertion in the fourth sentence above does belong here.
Aborts can and will cause task objects to stop existing. However, in
the normal case it will not be the named task objects which disappear,
but their children. The Abort_Task procedure takes one argument, and
no task can be its own master, so normally the task object (and the
Task_ID) will continue to be valid after the call--assuming we sort
this issue out right.
Robert I. Eachus
with Standard_Disclaimer;
use Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...
****************************************************************
!section 13.9.1(08)
!subject Is it always erroneous to test 'TERMINATED for an aborted task?
!reference RM95 13.9.1(8);6.0
!reference RM95 9.8(1);6.0
!reference RM95 9.8(4);6.0
!reference RM95 9.9(1);6.0
!reference RM95-C.7.1(9);6.0
!reference RM95-C.7.1(18);6.0
!reference 95-5348.a Robert I. Eachus 95-10-17
!reference 95-5349.a Tucker Taft 95-10-18
!reference 95-5350.a Robert I. Eachus 95-10-18
!reference 95-5353.a Robert A Duff 95-10-19
!from Robert I. Eachus 95-10-20
!reference 95-5360.a Robert I. Eachus 95-10-20>>
!discussion
Bob Duff said:
> I think you are missing the fact that 13.9.1 defines "abnormal
> object", whereas 9.8 defines "abnormal task". A task is not an
> object. A task object is not a task, but *contains* a task.
> Therefore, it is clear that aborting a task DOES NOT make task
> objects abnormal (in the 13.9.1 sense). Aborting a task makes
> *tasks* abnormal, which has nothing to do with 13.9.1.
I keep wondering what all the fuss is about. I agree that this is
what we want the RM to say. But unless someone can point out where
the RM specifically says that the task object and task are separate
entities, and that the task object denotes the task, we need a BI. I
am not arguing that what Bob advocates is wrong, or not what is
intended. It is just not in the Ada 95 RM.
I'm sorry, I just regard this as a minor "easy" AI that must be a
binding interpretation because the words that would make it a
ramification are nowhere to be found--or not to be found in the Ada 95
RM. The sentences I would like to quote ARE in the Ada 83 RM:
RM83 9.2(2): "A task object is an object whose type is a task type.
The value of a task object designates a task that has entries of the
corresponding task type."
RM83 9.3(1): "A task body defines the execution of any task that is
designated by a task object of the corresponding task type."
Now read RM95 9.1(15-18) and RM95 9.2(2). I conclude from
9.1(15-18) that a task object DOES NOT contain or denote a task, it
contains among other things a representation of the state of the task.
I also conclude from 9.2 that a task object represents ONE task, but
no mention of denoting a task. Now go back to RM95 9.8(5): "...the
named task is then aborted, which consists of making the task
abnormal..." The dependence on names here is the problem. The name
is the name of a task object. See for example, RM95 9.1(10) where
explicitly the name in a single task declaration is the name of a task
object. Since nowhere is it said that the name denotes both a task
object and a task, we need a fix, or the object, which is the only
thing denoted, is what becomes abnormal. (The same thing applies in
9.9(2&3) where tasks denoted by a name are explicitly mentioned.)
Robert I. Eachus
with Standard_Disclaimer;
use Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...
****************************************************************
!section C.7.1(03)
!subject Why does Abort_Task have an in out parameter?
!reference RM95-C.7.1(3);6.0
!reference Norman Cohen 95-09-06
!reference 95-5325.a Robert A Duff 95-10-11
!reference 95-5331.a Offer Pazy 95-10-12
!reference 95-5339.a Ted Baker 95-10-13
!reference 95-5343.a Robert I. Eachus 95-10-16
!from Robert I. Eachus 95-10-22
!reference 95-5363.a Robert I. Eachus 95-10-22>>
!discussion
Assuming that everyone is agreed that there is a distinction
between task objects and tasks, which one does a Task_ID identify?
I have been assuming that a Task_ID identifies the task, not the
task object. Since 13.11.2(9-15) implies that there is no problem
deallocating unterminated tasks without discriminants, and the bounded
error conditions for the discriminated case involve references to the
discriminants, I assume that Ada.Task_Identification.Current_Task is
not permitted to fail just because the task object does not exist.
(RM95 C.7.1(18) specifically does not apply.)
My mental image is that Task_ID returns a system defined pointer
to the base of the task stack, and pointers to the code and task
object are located there. (Actually, if the task object has pointer
to the code entry point, you don't need one on the stack, but that is
a detail.) One alternative implementation would have Current_Task
return a pointer to the task object, and Unchecked_Deallocation would
never be allowed to deallocate the task object for an unterminated
task. The other alternative is to have Current_Task return a pointer
to the task object even if it no longer exists. Since it is erroneous
to use the Task_ID in that case there is no problem, at least from a
legality standpoint. Which is right? Or are all three permitted?
If the first implementation is legal, then Abort_Task should be
allowed to set T to Null_Task_ID.
****************************************************************
Questions? Ask the ACAA Technical Agent