Version 1.2 of ais/ai-00266.txt

Unformatted version of ais/ai-00266.txt version 1.2
Other versions for file ais/ai-00266.txt

!standard D.2.2 (5)          01-10-05 AI95-00266/02
!standard D.7 (00)
!class amendment 01-05-10
!status work item 01-05-10
!status received 01-05-10
!priority High
!difficulty Medium
!subject Task Termination procedure
A pragma is proposed that used as part of a task definition specifies a "task group" object to be notified when a task terminates.
In Ada 95, a task propagating an exception will silently be terminated. This is a significant hazard in high integrity systems.
An abstract type Root_Task_Group is declared as follows:
with Ada.Task_Identification; with Ada.Exceptions; package Ada.Task_Groups is type Root_Task_Group is abstract tagged limited private; procedure Aborted(TG : in out Root_Task_Group; ID : Task_Identification.Task_ID) is abstract; -- Called upon task termination, -- if it completed due to abortion. procedure Unhandled_Exception(TG : in out Root_Task_Group; ID : Task_Identification.Task_ID; Excep : Exceptions.Exception_Occurrence) is abstract; -- Called upon task termination, -- if it completed due to an unhandled exception procedure Normal_Termination(TG : in out Root_Task_Group; ID : Task_Identification.Task_ID) is abstract; -- Called upon task termination, -- if it completed due to completing the last statement -- of the task body, or as part of waiting on a terminate -- alternative
private -- Not specified by the language end Ada.Task_Groups;
A new pragma is defined:
pragma Task_Group(task-group_name [, On => object-or-type_local_name]);
The task-group_name argument must denote a variable object whose type is covered by Root_Task_Group'Class. The On argument, if present, must statically denote an object or first subtype declared immediately in the current region.
When the On argument is present, the Task_Group pragma directly specifies the task-group aspect for the object or type denoted by the argument. A derived type inherits the task-group aspect of its parent type. An object inherits the task-group aspect of its type. An object created by an allocator inherits the task-group aspect of the type of the allocator. If both the object's type and the allocator type have a specified task-group, the object inherits the task-group from the type of the allocator. A component object inherits the task-group aspect from its enclosing object. If both the enclosing object and its type specify the task-group, the component inherits the task-group aspect from its enclosing object.
When the On argument is not present, the Task_Group pragma establishes a "default" task-group aspect for all declarations within the immediate scope of the pragma. A Task_Group pragma without an On argument in an inner scope overrides the default established in an outer scope. If the task-group aspect is specified for a task, this becomes the default for the outermost region of its task body.
If a task object has a task-group aspect specified directly or indirectly, then immediately prior to task termination, after the task body has completed and been left, a call is made to one of the three operations of the associated task-group object. If the task completes due to an abort statement, the Aborted operation is called. If the task completes due to an unhandled exception, the Unhandled_Exception operation is called. If the task completes due to completing the last statement of the task body, or as part of waiting on a terminate alternative, the Normal_Termination operation is called. In each case, the ID parameter identifies the task that is terminating. For the Unhandled_Exception operation, the Excep parameter holds the Exception_Occurrence which was not handled.
The result of calling Current_Task while executing one of the task-group operations identifies the task about to terminate. Both the Terminated attribute and the Callable attribute of the task return False.
Because the task has not yet terminated at the time of the call, task attributes created by an (active) instantiation of the Ada.Task_Attributes package have not yet been finalized, allowing the operations to read information from these attributes.
Note that concurrent calls on task-group operations are possible due to concurrent termination of tasks. Hence, these operations should be implemented in a reentrant manner.
Many safety critical and high integrity systems prohibit exception handling, and so the use of a "when others" handler at the task body level is then not available.
We have proposed a technique that is reminiscent of what Java does, in that it establishes a separate abstraction, called a task group ("thread group" in Java) which receives notification of task termination.
This proposal allows each library package to specify a default task-group, as well as allowing more local specifications. The proposal does not provide any direct way to specify the task-group for all tasks in the entire program. This is difficult due to elaboration order issues. Multiple packages are allowed to specify the same task-group object, since the task-group_name argument of the pragma is a "name" rather than a "local_name".
Because task terminations can occur immediately after task activation, it is important that the operations of the task-group object be elaborated prior to activations being performed. Use of pragma Elaborate_All on the package containing the type of the task-group object is therefore recommended.
!ACATS test

[Editor's note: This originally was part of the Ravenscar proposal,
see AI-249.]


From: Randy Brukardt
Date: Wednesday, April 11, 2001, 5:41 PM.

This proposal seems to be exactly equivalent to the following code. (We use
code like this in Claw to handle locking, that the user can't lock
something and leave it locked by omission or by abnormal termination.)

Since this code is pure Ada 95, it would be useful to understand why this
is not an effective solution to the problem posed.

     type Handler_Object is new Ada.Finalization.Limited_Controlled
         with null record;
     procedure Finalize (Object : in out Handler_Object) is
         <handler_name>; -- Call to <handler_name>; as a practical matter,
             -- you'd probably put the code here.
     end Finalize;

     task My_Task ...

     task body My_Task is
         Termination_Handler : Handler_Object; -- Declare this first,
               -- so it gets finalized last.
     end My_Task;

The basic idea is to insure that the last thing Finalized is your
termination handler. Clearly, it's easier to mess this up than with the
pragma (if someone sticks something in front of the termination object, you
could have trouble), but the semantics seem to be exactly the same. So I
would consider this proposal unnecessary without further justification. I'd
also suggest that the semantics be defined in terms of this sort of code if
possible (it would make the proposal look "simpler", which is always good).


Questions? Ask the ACAA Technical Agent