Rationale for Ada 2005
5.2 Task termination
In the Introduction
(in Section 1.3.4
) we mentioned the problem
of how tasks can have a silent death in Ada 95. This happens if a task
raises an exception which is not handled by the task itself. Tasks may
also terminate because of going abnormal as well as terminating normally.
The detection of task termination and its causes can be monitored in
Ada 2005 by the package Ada.Task_Termination
whose specification is essentially
type Cause_Of_Termination is (Normal, Abnormal, Unhandled_Exception);
type Termination_Handler is access protected
procedure(Cause: in Cause_Of_Termination;
T: in Task_Id; X: in Exception_Occurrence);
procedure Set_Dependents_Fallback_Handler (Handler: in Termination_Handler);
function Current_Task_Fallback_Handler return Termination_Handler;
procedure Set_Specific_Handler(T: in Task_Id; Handler: in Termination_Handler);
function Specific_Handler(T: in Task_Id) return Termination_Handler;
(The above includes use clauses in order to simplify
the presentation; the actual package does not have use clauses. The other
predefined packages described in this chapter are treated similarly.)
The general idea is that we can associate a protected
procedure with a task. The protected procedure is then invoked when the
task terminates with an indication of the reason passed via its parameters.
The protected procedure is identified by using the type Termination_Handler
which is an access type referring to a protected procedure.
The association can
be done in two ways. Thus (as in the Introduction) we might declare a
protected object Grim_Reaper
protected Grim_Reaper is
procedure Last_Gasp(C: Cause_Of_Termination; T: Task_Id; X: Exception_Occurrence);
which contains the protected procedure Last_Gasp.
Note that the parameters of Last_Gasp match
those of the access type Termination_Handler.
We can then nominate
Last_Gasp as the protected procedure to be
called when the specific task T dies by
Alternatively we can
nominate Last_Gasp as the protected procedure
to be called when any of the tasks dependent on the current task becomes
terminated by writing
Note that a task is not dependent upon itself and
so this does not set a handler for the current task.
Thus a task can have two handlers. A fallback handler
and a specific handler and either or both of these can be null. When
a task terminates (that is after any finalization but just before it
vanishes), the specific handler is invoked if it is not null. If the
specific handler is null, then the fallback handler is invoked unless
it too is null. If both are null then no handler is invoked.
The body of protected
procedure Last_Gasp might then output various
diagnostic messages to a log for later analysis, thus
procedure Last_Gasp(C: Cause_Of_Termination; T: Task_Id; X: Exception_Occurrence) is
case C is
when Normal => null;
when Abnormal =>
Put_Log("Something nasty happened to task ");
when Unhandled_Exception =>
Put_Log("Unhandled exception occurred in task ");
There are three possible reasons for termination,
it could be normal, abnormal (caused by abort), or because of propagation
of an unhandled exception. In the last case the parameter X
gives details of the exception occurrence whereas in the other cases
X has the value Null_Occurrence.
Initially both specific and fallback handlers are
null for all tasks. However, note that if a fallback handler has been
set for all dependent tasks of T then the
handler will also apply to any task subsequently created by T
or one of its descendants. Thus a task can be born with a fallback handler
already in place.
If a new handler is set then it replaces any existing
handler of the appropriate kind. Calling either setting procedure with
null for the handler naturally sets the appropriate handler to null.
The current handlers can be found by calling the functions Current_Task_Fallback_Handler
or Specific_Handler; they return null if the
handler is null.
It is important to realise that the fallback handlers
for the tasks dependent on T need not all
be the same since one of the dependent tasks of T
might set a different handler for its own dependent tasks. Thus the fallback
handlers for a tree of tasks can be different in various subtrees. This
structure is reflected by the fact that the determination of the current
fallback handler of a task is in fact done by searching recursively the
tasks on which it depends.
Note that we cannot directly interrogate the fallback
handler of a specific task but only that of the current task. Also, if
a task sets a fallback handler for its dependents and then enquires of
its own fallback handler it will not in general get the same answer since
it is not one of its own dependents.
Remember the situation regarding the environment
task. This unnamed task is the task that elaborates the library units
and then calls the main subprogram. Library tasks (that is tasks declared
at library level) are activated by the environment task before it calls
the main subprogram.
Suppose the main subprogram
calls the setting procedures as follows
procedure Main is
protected RIP is
procedure One( ... );
procedure Two( ... );
The specific handler for the environment task is
then set to Two (because Current_Task
is the environment task at this point) but the fallback handler for the
environment task is null. On the other hand the fallback handler for
all other tasks in the program including any library tasks is set to
One. Note that it is not possible to set the
fallback handler for the environment task.
The astute reader will
note that there is actually a race condition here since a library task
might have terminated before the handler gets set. We could overcome
this by setting the handler as part of the elaboration code thus
package Start_Up is
with Ada.Task_Termination; use Ada.Task_Termination;
package body Start_Up is
package Library_Tasks is
... -- declare library tasks here
Note how the use of pragmas Elaborate_Body
and Elaborate ensures that things get done
in the correct order.
Some minor points are that if we try to set the specific
handler for a task that has already terminated then Tasking_Error
is raised. And if we try to set the specific handler for the null task,
that is call Set_Specific_Handler with parameter
T equal to Null_Task_Id,
then Program_Error is raised. These exceptions
are also raised by calls of the function Specific_Handler
in similar circumstances.
© 2005, 2006, 2007 John Barnes Informatics.
Sponsored in part by: