CVS difference for ais/ai-10266.txt

Differences between 1.4 and version 1.5
Log of other versions for file ais/ai-10266.txt

--- ais/ai-10266.txt	2002/07/23 01:05:25	1.4
+++ ais/ai-10266.txt	2003/05/24 00:41:06	1.5
@@ -1,4 +1,4 @@
-!standard D.2.2 (5)                                02-06-01  AI95-00266-02/01
+!standard D.2.2 (5)                                03-03-03  AI95-00266-02/02
 !standard D.7 (00)
 !class amendment 01-06-01
 !status work item 01-06-01
@@ -9,12 +9,14 @@
 
 !summary
 
-A mechanism is proposed for associating procedures or protected procedures
+A mechanism is proposed for associating protected procedures
 with a task. These procedures are invoked when the task is about to terminate
 (either normally, as a result of an unhandled exception or due to abortion).
 If a task terminates due to an unhandled exception, the exception occurrence
 is passed as a parameter to the procedure. The proposal provides an alternative
-to that of AI95-00266-01 which was rejected by IRTAW11.
+to that of AI95-00266-01 which was rejected by IRTAW11; it responds to earlier
+comments by the the ARG. The proposal here introduces default procedures
+but does not go as far as task groups.
 
 !problem
 
@@ -29,64 +31,49 @@
 with Ada.Exceptions; use Ada.Exceptions;
 package Ada.Task_Termination is
 
-  type Termination_Handler is access procedure (
-              Id: Task_Id);
-  type Exceptional_Termination_Handler is access procedure (
-              Id: Task_Id; X: Exception_Occurrence);
-
-
-  procedure Set_Normal_Termination_Handler(
-                Handler : Termination_Handler;
-                Id : Task_Id := Current_Task);
-              -- The Handler is 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.
-
-
-  procedure Set_Unhandled_Exception_Handler(
-                Handler: Exceptional_Termination_Handler;
-                Id : Task_Id := Current_Task);
-              -- The Handler is called immediately prior to task termination,
-              -- if the task completed due to an unhandled exception during
-              -- execution or activation.
-
-  procedure Set_Abnormal_Termination_Handler(
-                Handler: Termination_Handler;
-                Id : Task_Id := Current_Task);
-              -- The Handler is called immediately prior to task termination,
-              -- if the task completed due to abortion.
-
-
-
   type Protected_Termination_Handler is access protected procedure(
               Id: Task_Id);
 
   type Protected_Exceptional_Termination_Handler is access protected procedure(
               Id: Task_Id; X: Exception_Occurrence);
 
-  procedure Set_Normal_Termination_Protected_Handler(
-                Handler: Protected_Termination_Handler;
-                Id : Task_Id := Current_Task);
-              -- The Handler is called immediately prior to task termination,
+  procedure Set_Normal_Termination_Handler(
+                New_Handler: in Protected_Termination_Handler;
+                Old_Handler: out Protected_Termination_Handler;
+                Id : in Task_Id := Current_Task);
+              -- New_Handler is 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.
 
+  procedure Set_Default_Normal_Termination_Handler(
+                New_Handler: in Protected_Termination_Handler
+                Old_Handler: out Protected_Termination_Handler);
 
-  procedure Set_Unhandled_Exception_Protected_Handler(
-                Handler: Protected_Exceptional_Termination_Handler;
-                Id : Task_Id := Current_Task);
-              -- The Handler is called immediately prior to task termination,
+
+  procedure Set_Unhandled_Exception_Handler(
+                New_Handler: in Protected_Exceptional_Termination_Handler;
+                Old_Handler: out Protected_Termination_Handler;
+                Id : in Task_Id := Current_Task);
+              -- New_Handler is called immediately prior to task termination,
               -- if the task completed due to an unhandled exception
               -- during execution or activation
 
-  procedure Set_Abnormal_Termination_Protected_Handler(
-                Handler: Protected_Termination_Handler;
-                Id : Task_Id := Current_Task);
-              -- The Handler is called immediately prior to task termination,
+  procedure Set_Default_Unhandled_Exception_Handler(
+                New_Handler: in Protected_Exceptional_Termination_Handler
+                Old_Handler: out Protected_Exceptional_Termination_Handler);
+
+  procedure Set_Abnormal_Termination_Handler(
+                New_Handler: in Protected_Termination_Handler;
+                Old_Handler: out Protected_Termination_Handler;
+                Id : in Task_Id := Current_Task);
+              -- New_Handler is called immediately prior to task termination,
               -- if the task completed due to abortion.
 
+  procedure Set_Default_Abnormal_Termination_Handler(
+                New_Handler: in Protected_Termination_Handler
+                Old_Handler: out Protected_Termination_Handler);
+
 end Ada.Task_Termination;
 
 !wording
@@ -116,18 +103,21 @@
 before execution in a Ravenscar environment would cause the program to fail
 and the problem would need to be handled at a different level.
 
-When any of the (protected) subprograms are called and the task
-has already terminated then there are three possible semantics to consider:
-  a) view as a null operation,
-  b) raise an exception,
-  c) call the handler immediately
-Option a) would seem inappropriate, as the task will have then died silently.
-Option b) may not be acceptable in a no-exception environment.
-Option (c) avoids any potential race conditions.
-The user-supplied procedure, in this case, is called from the task
-which executed the associated subprogram.
+This proposal allows defaults (for the active partition) to be assigned
+for all tasks; or per-task handlers to be set. It does not attempt to
+define task groups. If a different AI were to propose a means of sensibly
+grouping tasks together then it would be possible to extend the provisions
+defined here. To give some level of support for composability the old
+handler is returned when a new one is set. This could be null if there
+is no previous handler.
+
+When any of the protected subprograms are set and the task
+has already terminated then the handler is executed immediately.
+
+If a null handler is set then this is equvalent to there being no
+handler.
 
-The requirement is that the user-supplied (protected) subprogram is usually
+The requirement is that the user-supplied protected subprogram is usually
 called after the task has been finalized but on the stack of the terminating
 task. Consequently, if Task_Identification.Current_Task is called from one of
 the subprograms, the terminating task identifier is returned.
@@ -136,14 +126,9 @@
 of the calling task, and Task_Identification.Current_Task
 returns the calling task.
 
-It is a bounded error for the user-supplied (protected) subprogram to
+It is a bounded error for the user-supplied protected subprogram to
 propagate an exception.
 
-A model that allows handlers to be attached and detached in a
-similar manner to that provided by Ada.Interrupts was considered
-but rejected for simplicity of implementation. In the current proposal,
-the setting of a handler over-writes any previous handler.
-
 An alternative model was considered whereby the following attributes were
 defined -- where T is a task type (after any implicit dereference):
 
@@ -173,97 +158,52 @@
 
 !example
 
-The following two examples illustrate how the mechanisms can be used.
+The following example illustrates how the mechanisms can be used.
 
-Example 1
-
-The first example is of a package that logs the failures of tasks due to
-unhandled exceptions.
+The example is of a package that logs the failures of tasks due to
+unhandled exceptions. It also releases a guardian task if any task
+fails due to being aborted.
 
 with Ada.Task_Identification; use Ada.Task_Identification;
 with Ada.Exceptions; use Ada.Exceptions;
-package Termination_Logging is
-  procedure Log_Non_Normal_Termination (
-     Id: Task_Id; X: Exception_Occurrence);
+package Termination_Logging is -- library package
+  protected Logger is
+    entry Guardian;
+    procedure Log_Non_Normal_Termination (
+      Id: Task_Id; X: Exception_Occurrence);
+    procedure Note_Abnormal_Termination(Id : Task_Id);
+  private
+    Abort_Occurrence : Boolean := False;
+  end Logger;
 end Termination_Logging;
 
 
 package body Termination_Logging is
-  procedure Log_Non_Normal_Termination (
-     Id: Task_Id; X: Exception_Occurrence) is
-  begin
-    -- write out error message to operator terminal
-    -- log event to file
-  end Log_Non_Normal_Termination;
-end Termination_Logging;
-
-
-
-A program fragment using this package is as follows:
-
-
-with Ada.Task_Identification; use Ada.Task_Identification;
-with Ada.Task_Termination; use Ada.Task_Termination;
-with Termination_Logging; use Termination_Logging;
-
-package body Ravenscar_Example is
-  task MyTask;
-  task body MyTask is
-  begin
-    Ada.Termination.Set_Unhandled_Exception_Handler(
-                    Log_Non_Normal_Termination'Access);
-    loop
-         . . .;
-    end loop;
-  end MyTask;
-end Ravenscar_Example;
-
-Example 2:
-
-The second example illustrate how a task can wait for the normal or
-abnormal termination of a task and be able to detect the difference.
-First consider a package which provides a synchronization agent.
-
-with Ada.Task_Identification; use Ada.Task_Identification;
-with Ada.Termination; use Ada.Termination;
-with Ada.Exceptions; use Ada.Exceptions;
-package Signals is
-    protected Signal is
-      procedure Normal_Termination(Tid : Task_Id);
-      procedure Abnormal_Termination(Tid: Task_Id);
-      procedure Exceptional_Termination(Tid : Task_Id;
-                                        Excep : Exception_Occurrence);
-      entry Wait_Termination(Normal: out Boolean);
-    private
-      Signal_Arrived : Boolean := False;
-      Normal : Boolean;
-    end Signal;
-end Signals;
-
-Whichever subprogram is called will open the barrier associated with
-the entry and set the Boolean out parameter accordingly.
-
-The package can be used as follows:
+  protected body Logger is
+    entry Guardian when Abort_Occurrence is
+    begin
+      Abort_Occurence := False;
+    end Guardian;
+    procedure Log_Non_Normal_Termination (
+      Id: Task_Id; X: Exception_Occurrence) is
+    begin
+      -- write out error message to operator terminal
+      -- log event to file
+    end Log_Non_Normal_Termination;
+    procedure Note_Abnormal_Termination(Id : Task_Id) is
+    begin
+      Abort_Occurrence := True;
+    end Note_Abnormal_Termination;
+  end Logger;
+  Old_Exception_Handler : Protected_Exceptional_Termination_Handler;
+  Old_Abnormal_Handler : Protected_Termination_Handler;
 
-with Signals; use Signals;
-with Ada.Task_Identification; use Ada.Task_Identification;
-with Ada.Task_Termination; use Ada.Task_Termination;
-procedure Another_Example is
-  task MyTask;
-  task body MyTask is
-  begin
-    . . .;
-  end MyTask;
-  Normal : Boolean;
 begin
-  Set_Abnormal_Termination_Protected_Handler(
-         Signal.Abnormal_Termination'Access,
-         MyTask'Identity);
-  -- similarly for normal termination, and exceptional termination;
-  Signal.Wait_Termination(Normal);
-  if(Normal) then . . .;
-end Another_Example;
-
+  Set_Default_Unhandled_Exception_Handler
+      (Logger.Log_Non_Normal_Termination'Access, Old_Exception_Handler);
+  Set_Default_Abnormal_Termination_Handler
+      (Logger.Note_Abnormal_Termination'Access, Old_Abnormal_Handler);
+end Termination_Logging;
 
 !ACATS test
 
@@ -1601,6 +1541,259 @@
 correctly, an object of size k+1 raises Storage_Error, and allocating an
 object of size k safely followed by another allocation is also raises
 Storage_Error and is correctly handled.)
+
+****************************************************************
+
+From: Alan Burns
+Sent: Tuesday, March 4, 2003  7:37 AM
+
+I attach a new version of the AI produced at the last IRTAW for task
+termination. The previous discussion got bogged down as some
+people saw the facility as a very general one that would help
+debugging, whilst others wanted a simple scheme for simple
+(Ravenscar-ish) programs.
+
+The attached leans toward the simple scheme. It does not
+attempt to define task groups (this is where there was much
+disagreement). It allows default handlers for all tasks in
+a partition or per-task handlers to be set.
+
+[This is version /02 - ED.]
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Tuesday, March 4, 2003  9:46 AM
+
+I wonder whether you could use a value of Null_Task_ID
+to mean "default" rather than having a separate
+set of Set_Default... routines.
+
+It also seems odd to make "Current_Task" be the
+default value for the task Id parameter.  I would
+almost always expect this to be set from "outside" the
+task.  If you adopt the idea that "Null_Task_ID" means
+"default" then it might also be a more useful
+default for the Id parameter than Current_Task.
+
+****************************************************************
+
+From: Gary Dismukes
+Sent: Tuesday, March 4, 2003  12:30 PM
+
+Alan,
+
+The Set_* operations in your proposal all take both a new and old
+handler (in and out formals), but in your example you're only passing
+an actual for the new handler.  Did you mean to define versions of
+the Set ops that don't require returning the old handler?
+
+I also note a couple of typos in your example:
+
+  "Abort_Occurence" should be spelled "Abort_Occurrence".
+
+  In the second Set_Default_ call, the actual should be:
+     Logger.Note_Abnormal_Termination'Access.
+
+> package body Termination_Logging is
+>   protected body Logger is
+>     entry Guardian when Abort_Occurence is
+>     begin
+>       Abort_Occurence := False;
+>     end Guardian;
+>     procedure Log_Non_Normal_Termination (
+>       Id: Task_Id; X: Exception_Occurrence) is
+>     begin
+>       -- write out error message to operator terminal
+>       -- log event to file
+>     end Log_Non_Normal_Termination;
+>     procedure Note_Abnormal_Termination(Id : Task_Id) is
+>     begin
+>       Abort_Occurence := True;
+>     end Note_Abnormal_Termination;
+>   end Logger;
+> begin
+>   Set_Default_Unhandled_Exception_Handler
+>       (Logger.Log_Non_Normal_Termination'Access);
+>>> missing actual for Old_Handler
+
+>   Set_Default_Abnormal_Termination_Handler
+>       (Logger.Log_Note_Abnormal_Termination'Access);
+>>> missing actual for Old_Handler
+
+> end Termination_Logging;
+
+I also agree with Tucker's comments about eliminating the Set_Default
+versions of the ops and using Null_Task_Id instead of Current_Task.
+
+****************************************************************
+
+From: Alan Burns
+Sent: Wednesday, March 5, 2003  2:57 AM
+
+Thanks for comments - the error in the example was just due
+to a last minute change (defaults also returning old handler)
+that did not propagate through to the example
+
+[Editor's note: The example has been corrected.]
+
+Using Null_Task_Id to set the defaults certainly simplifies
+the package. But I guess it sort of opens up a potential error
+(failing to put the task ID results in default being set).
+Setting default and setting a task are quite different operations;
+should this difference be identified by a parameter or by the use
+of a differently named set of procedures? Does ARG have a view on
+this sort of issue?
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Wednesday, March 5, 2003  2:57 PM
+
+The existing proposal seems to have a similar danger
+where the programmer leaves out the task ID and
+presumes it is setting it for all tasks, or all
+sub-tasks, or something.
+
+I guess I find it pretty natural that if you leave
+off the task ID, it defaults to meaning all tasks.
+That certainly makes more sense to me than it meaning
+the current task.
+
+I wonder now whether the default should be not for all
+tasks, but for all subtasks?  The environment task would
+set the default for all non-environment tasks, other tasks
+could set the default for their own subtasks only.
+Presumably subtasks would inherit the default from
+their master task unless the master task set up
+its own default.
+
+This would give a kind of task group capability, based
+on the existing grouping that Ada already provides,
+namely all the subtasks of a given task.  Of course
+it would be irrelevant to Ravenscar since it doesn't
+allow subtasks, but it would seem a natural capability
+for systems that do allow subtasks.
+
+****************************************************************
+
+From: Alan Burns
+Sent: Friday, March 7, 2003  9:25 AM
+
+I wonder if this extra functionality is worth the extra
+overhead - a full hierarchy of tasks will lead to many
+defaults. The relationship will need to be on master as
+a change of default will need to impact on dependent tasks
+in the future and ones previously declared. This does sound
+complicated. Is there really a need for this? I remember
+the previous ARG discussion where blocks and subprograms
+as masters were introduced (in the sense of being able
+to set defaults for dependent tasks) - the discussion
+raised many more problems than solutions.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, March 7, 2003  10:02 AM
+
+This is only for run-times that support
+hierarchies.  But I agree, it is a good question
+whether this kind of functionality is appropriate.
+You mentioned the issue related to task groups,
+and that made me think that Ada already has a task
+hierarchy, meaning that there is no real need
+to introduce another grouping mechanism.
+
+Hence it seems to me that establishing default behavior
+for task termination, if grouped at all, should be based
+on the existing Ada task hierarchy concepts.
+
+Again this is focusing on what makes sense in a full
+functionality run-time, as opposed to Ravenscar.
+It seems in that situation, establishing defaults within
+a subtree of the task hierarchy makes a lot of sense,
+and it is quite simple to describe.  I don't see much
+implementation burden or distributed overhead, since task
+termination is relatively rare, and recording the default
+handler for the subtasks in the parent task's TCB seems
+pretty straightforward.  Inheritance can be implemented
+either in an "eager" fashion when the default is set
+or a subtask is created, or in a "lazy" fashion by scanning
+up through ancestor tasks on task termination.  A global
+flag could be kept to remember whether there are any
+termination handlers at all in the program.
+
+****************************************************************
+
+From: Alan Burns
+Sent: Monday, March 10, 2003  6:36 AM
+
+Ok, I'll change the AI to reflect this, but a couple of minor
+questions Tuck
+
+1. Can you get the task_id of the environment task - so that the
+default for a whole program can be set
+
+2. if the default is changed to an existing hierarchy of tasks
+does it impact on all these tasks - even the ones that have
+themselves set their default?
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Monday, March 10, 2003  9:38 AM
+
+Alan Burns wrote:
+> ...
+> Ok, I'll change the AI to reflect this, but a couple of minor
+> questions Tuck
+>
+> 1. Can you get the task_id of the environment task - so that the
+> default for a whole program can be set
+
+I'm not sure I understand this question.  To set the default for the
+library-level tasks (those with the environment task as master), the
+environment task would call the Set_... procedure with null task ID.
+To set the handler for the environment task itself (which seems
+a bit weird), you would pass in the task ID for the environment
+task, which is what Current_Task returns when the environment task
+calls it.
+
+> 2. if the default is changed to an existing hierarchy of tasks
+> does it impact on all these tasks - even the ones that have
+> themselves set their default?
+
+I am again getting confused by the term "default."  I presumed either
+you set the *handler* for a particular task, or you set the *default handler*
+for all subtasks of the calling task.  A default would never override
+a handler associated with a specific task, nor would it override a default
+set by a subtask to control its further subtasks.  It shouldn't matter
+in what order the handlers are set, except of course if a handler is
+set a second time for the same specific task, or if a default handler
+is specified a second time for the same calling task's subtasks.
+
+As an example, presume we have library-level tasks A, B, and C,
+each with subtasks, A1..A3, B1..B3, and C1..C3 respectively.
+When task A1 terminates, it would first see whether there is
+a specific handler for A1 itself, then see if there is a default
+handler set by its master task (A), and if not, then see if there
+was a default set by A's master task (the environment task).
+What matters is what has been set up by the time A1 terminates, not
+the order in which these things are set up.
+
+****************************************************************
+
+From: Alan Burns
+Sent: Tuesday, March 11, 2003  2:46 AM
+
+My email was not clear - I was concerned with the situation that
+a default is set for the second time. The model you have in which
+a default is only searched for at the time of termination (rather
+than all tasks having defaults inherited from master task) works
+fine and does not have the problem of the second setting of a default
+having to change the defaults of all dependent tasks.
+
+I'll do the AI
 
 ****************************************************************
 

Questions? Ask the ACAA Technical Agent