CVS difference for ais/ai-00266.txt

Differences between 1.2 and version 1.3
Log of other versions for file ais/ai-00266.txt

--- ais/ai-00266.txt	2001/10/09 00:47:10	1.2
+++ ais/ai-00266.txt	2002/02/07 04:56:42	1.3
@@ -1,4 +1,4 @@
-!standard D.2.2 (5)                                01-10-05  AI95-00266/02
+!standard D.2.2 (5)                                02-02-05  AI95-00266/03
 !standard D.7 (00)
 !class amendment 01-05-10
 !status work item 01-05-10
@@ -9,8 +9,12 @@
 
 !summary
 
-A pragma is proposed that used as part of a task definition specifies
-a "task group" object to be notified when a task terminates.
+A mechanism is proposed for associating a task group object with a task.
+The task group object receives notification when the task is about to
+terminate. If a task terminates due to an unhandled exception,
+the exception occurrence is passed to the group. A task inherits
+the task group of its master at the time of creation, but the
+task group may be changed during the task's lifetime.
 
 !problem
 
@@ -21,106 +25,267 @@
 
 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
+        with Ada.Task_Identification;
+        with Ada.Exceptions;
+        with Ada.Finalization;
+        package Ada.Task_Groups is
+
+            type Root_Task_Group is abstract tagged limited private;
+
+            procedure Unhandled_Exception(TG : access Root_Task_Group;
+                        ID : Task_Identification.Task_ID;
+                        Excep : Exceptions.Exception_Occurrence) is abstract;
+              -- Called immediately prior to task termination,
+              -- if the task completed due to an unhandled exception.
+              -- This operation must be overridden.
+
+            procedure Aborted(TG : access Root_Task_Group;
+                        ID : Task_Identification.Task_ID); -- is null;
+              -- Called immediately prior to task termination,
+              -- if the task completed due to abortion.
+              -- By default, this operation does nothing.
+
+            procedure Normal_Termination(TG : access Root_Task_Group;
+                        ID : Task_Identification.Task_ID); -- is null;
+              -- Called immediately prior to task termination,
+              -- if the task completed due to completing the last statement
+              -- of the task body, or as part of waiting on a terminate
+              -- alternative.
+              -- By default, this operation does nothing.
+
+            procedure Never_Activated(TG : access Root_Task_Group;
+                        ID : Task_Identification.Task_ID); -- is null;
+              -- Called when a task is aborted before it
+              -- is activated, by the would-be activator
+              -- at the normal point of activation (immediately
+              -- after the "begin").
+              -- By default, this operation does nothing.
+
+
+            type Task_Group_Access is
+              access all Root_Task_Group'Class;
+                -- Reference to task group object
+
+            procedure Specify_Task_Group(
+              New_Group : in Task_Group_Access;
+              Old_Group : out Task_Group_Access;
+              ID : in Task_Identification.Task_ID :=
+                Task_Identification.Current_Task);
+                -- Specify the task group for the identified task.
+                -- Its current task group is returned in Old_Group.
+
+            function Current_Task_Group(
+              ID : in Task_Identification.Task_ID :=
+                Task_Identification.Current_Task) return Task_Group_Access;
+                  -- Return the current task group of the identified task.
+                  -- If this returns null at the time of termination, then
+                  -- the current default task group (see below) will be
+                  -- notified of termination.
+
+
+            procedure Specify_Default_Task_Group(
+              New_Default_Group : in Task_Group_Access;
+              Old_Default_Group : out Task_Group_Access);
+                -- Specify the task group to receive notifications
+                -- from tasks that terminate when they have a null task group.
+                -- The current "default" task group is returned in
+                -- Old_Default_Group, and is initially null.
+
+            function Current_Default_Task_Group return Task_Group_Access;
+                -- Return the current default task group
+
+
+            type Local_Task_Group(
+              Group : access Root_Task_Group'Class) is
+                new Ada.Finalization.Limited_Controlled with private;
+                  -- Declaring an object of this type has the effect
+                  -- of establishing a local task group for current task;
+                  -- Upon finalization, the original task group is restored.
+
+        private
+            -- Not specified by the language
+        end Ada.Task_Groups;
+
+Upon termination, the task group for a task is notified.
+The task group for a task is determined as follows:
+
+   a) The task group for the environment task is initially null;
+
+   b) The task group for a task created by a declaration
+      is set to that of its master task at the time of creation;
+
+   c) The task group for a task created by an allocator is set
+      to the group of its master task at the time of the elaboration
+      of the ultimate ancestor of the access type.
+
+   d) The task group for a task can be set (or reset) by a call on
+      Specify_Task_Group identifying the task, or (for the current
+      task) by elaborating or finalizing an object of type Local_Task_Group.
+
+   e) Upon termination, if the task group is still null, then
+      the current "default" task group is used, as specified by the most
+      recent call on "Specify_Default_Task_Group."
+
+In other words, a task typically inherits the task group of its master upon
+creation, can have its group (re)set explicitly during its lifetime, and
+defaults to the "default task group" upon termination.
+
+Notifying a task group of (impending) termination consists of calling the
+appropriate primitive operation of the task group, as determined by how the
+task terminates.  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.
+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.
+
+If a task never begins execution (because it is aborted prior to activation),
+then the Never_Activated operation of its task group is called by its activator
+at the point where it would have been activated (i.e. immediately
+after the "begin").  It is unspecified whether some or all of its (unaborted)
+"sibling" tasks are activated prior to Never_Activated being called.
+
+The result of calling Current_Task while executing Never_Activated will
+identify the activator, and the Terminated attribute of the task will be True.
+For the other three operations, the result of calling Current_Task while
+executing one of the task-group operations identifies the task about to
+terminate, and the Terminated attribute of the task will be False.
+In either case, the Callable attribute of the task will be False.
+
+When Unhandled_Exception, Aborted, or Normal_Termination are called,
+task attributes created by an (active) instantiation of the
+Ada.Task_Attributes package have not yet been finalized, allowing these
+operations to read information from the attributes.
+Inside Never_Activated, the task attributes are not available.
 
 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.
 
-!wording
+To support local scopes where a distinct task group is to be used,
+we have provided a limited controlled type, called "Local_Task_Group,"
+whose objects establish a specified task group when elaborated, and restore
+the old task group when finalized.  We provide this mechanism because
+it is safer than explicitly calling Specify_Task_Group before and after the
+scope, as it works even if the local scope propagates an exception
+or is aborted, and works safely with stack-allocated task groups.
+
+Note that Local_Task_Group may also be used in a library-level
+package to set the task group for library-level tasks.  The task
+group setting will persist until it is overridden by some subsequent
+elaboration of a Local_Task_Group object, or an explicit
+call on Specify_Task_Group for the environment task.  Hence, if this
+approach is used to manage library level tasks, such a Local_Task_Group
+object should appear at the beginning of every package spec or body
+that declares tasks.
 
+!wording
 
-!example
 
 !discussion
 
 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.
+available. Furthermore, there may be many tasks in a system, and
+a systematic, centralized way of handling unhandled exceptions is
+preferred to having to repeat code in every task body.
+
+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.
+However, this proposal allows a task to change its task group
+after activation.  A task inherits the task group of its master
+task whenever its master task's group is non-null and the task's
+group is null.
+
+This proposal allows each task to have its own task group, but also
+allows tasks to inherit the task group from its master task upon
+creation.  This gives a combination of fine control when needed,
+along with the convenience of inheritance otherwise.
+
+We only inherit the task group from the master at the point of
+creation, so there is less chance of a task outliving its task group.
+For a task created by an allocator, we use the task group the master
+had at the time the access type was elaborated, again, to minimize
+the chance of having the task outlive its task group.
+
+We allow a task to change its task group during execution so
+that an inner scope can establish a local task group,
+wait for all local tasks to terminate, and then restore the old
+task group.  The Local_Task_Group controlled type provides this
+capability in a way that minimizes the chance of dangling references.
 
-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".
+We provide a default task group to allow most or all of the tasks
+in a program to share a single group, if appropriate.
 
 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.
+We considered having two task groups associated with a given
+task, one for the task itself to notify of its termination, and
+one to be the task group to be used for subtasks created that have
+this task as a master.  This would simplify things
+in one way, since the group to use for the task itself would not
+likely change during its lifetime.  However, the group to use for
+new subtasks would still need to be saved and restored around local
+scopes, and by the time the task terminated, the task's own
+group and the group for subtasks would likely end up being the
+same again.  Hence, we felt it was overall simpler to use a single group
+for both purposes.  [This decision might deserve revisiting, since
+it does introduce some possibility of dangling references as a result
+of calling Current_Task_Group on a task that has temporarily changed
+its task group to be a local task group.  Perhaps Current_Task_Group
+should not take a task ID, and Specify_Task_Group should not return
+the old group (given that the Local_Task_Group object does an
+automatic save/restore).  Alternatively, we would have two, but
+Local_Task_Group would be the only way to affect the "subtask
+creation group" while, Specify_Task_Group and Current_Task_Group
+would refer to the task's "own" group.]
+
+We considered a mechanism using pragmas or attributes to specify the
+task group associated with a scope, but the mechanism became too heavy.
+The Local_Task_Group controlled type provides much of the same
+capability without having to define pragmas or attributes.
+
+!example
+
+  package MTG is
+    type My_Task_Group is new Task_Groups.Root_Task_Group with ...
+
+    procedure Unhandled_Exception(TG : access My_Task_Group;
+      ID : Task_Identification.Task_ID;
+      Excep : Exceptions.Exception_Occurrence);
+        -- Handle notifications of unhandled exceptions
+
+        -- other operations of task group not overridden
+
+    function Num_Unhandled(TG : access My_Task_Group) return Natural;
+      -- Return count of how many tasks died due to unhandled exceptions
+  end MTG;
+
+  with MTG;
+  procedure Proc_With_Local_Tasks(...) is
+    Grp : aliased MTG.My_Task_Group;  -- Local task group object
+  begin
+    declare
+      -- Establish local task group
+      LTG : Task_Groups.Local_Task_Group(Grp'Access);
+
+      -- Declare some local tasks
+      task Local_Task1 is ...
+      task Local_Task2 is ...
+    begin
+      ... -- if Local_Task1 or Local_Task2 dies due to an unhandled excep
+          -- will call Unhandled_Exception(Grp, ...)
+
+    end; -- old task group restored implicitly at this point
+
+    if MTG.Num_Unhandled(Grp'Access) > 0 then
+        ... -- we got problems, report them
+    end if;
+
+  end Proc_With_Local_Tasks;
+
 
 !ACATS test
 

Questions? Ask the ACAA Technical Agent