!standard D.2.2 (5) 02-06-01 AI95-00266-02/01
!standard D.7 (00)
!class amendment 01-06-01
!status work item 01-06-01
!status received 01-06-01
!priority High
!difficulty Medium
!subject Task Termination procedure
!summary
A mechanism is proposed for associating procedures or 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.
!problem
In Ada 95, a task propagating an exception will be silently terminated.
This can be a significant hazard in high integrity systems.
!proposal
A package for handling task termination is defined as follows.
with Ada.Task_Identification; use Task_Identification;
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,
-- 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_Protected_Handler(
Handler: Protected_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_Protected_Handler(
Handler: Protected_Termination_Handler;
Id : Task_Id := Current_Task);
-- The Handler is called immediately prior to task termination,
-- if the task completed due to abortion.
end Ada.Task_Termination;
!wording
!discussion
Many safety critical and high integrity systems prohibit (or discourage)
exception handling, and so the use of a "when others" handler at the
outer most level of the task body level is then not 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. The proposed solution is applicable
at the general language level but it is also appropriate for a
Ravenscar environment.
The package could be a child of Ada.Task_Identification.
The primary goal of this proposal is to address only the issue of silent
termination. Consequently, the proposal does not address issues of
termination as a result of failures after task creation but before task
activation. It is not clear how useful detection of failure after creation
but before activation is, as the declarative region in which the task is
being created could fail before the creation point. Hence, the order of
elaboration of the declarative region containing the task would dictate
whether or not the event would be detected. Note also, failure of task
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.
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.
If the task has already terminated, the handler is called on the stack
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
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):
T'Unhandled_Exception_Handler
T'Abnormal_Termination_Handler
T'Normal_Termination_Handler
T'Activation_Failure_Handler
T'Termination_Without_Activation_Handler
These would return subprograms similar to that defined above.
They can be set via attribute_definition_clauses. This gives more
functionality than the selected approach (in particular it allows failures
before task activation to be handled). However, it has more impact on
the language definition. The approach was rejected because a) of the
language impact, b) the assumed target market here is a Ravenscar
environment and failures before the tasks begin their execution would
cause the program to fail, and c) the added functionality is of questionable
use.
Another drawback of the attribute approach is that it doesn't scale up to
applications where tasks are allocated dynamically (at least not if the
program needs to associate a different termination hook with different
dynamically allocated tasks of the same task type).
A pragma-based approach has the same set of issues as the attribute
approach.
!example
The following two examples illustrate 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.
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);
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:
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;
!ACATS test
!appendix
!standard D.2.2 (5) 02-04-12 AI95-???
!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
!from A. Burns and A.J. Wellings on behalf of IRTAW11
!summary
A mechanism is proposed for associating procedures or 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 which was rejected by IRTAW11. The reasons for the
rejection are summarised by the rapporteur's report which is attached
as an appendix to this AI. Whilst there might be some discussion on
the validity of these reasons (see appendix), in summary, the overall
feeling at the workshop was that the proposal was a "sledge hammer
to crack a nut".
!problem
In Ada 95, a task propagating an exception will be silently terminated.
This can be a significant hazard in high integrity systems.
!proposal
A package for handling task termination is defined as follows.
with Ada.Task_Identification; use Task_Identification;
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,
-- 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_Protected_Handler(
Handler: Protected_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_Protected_Handler(
Handler: Protected_Termination_Handler;
Id : Task_Id := Current_Task);
-- The Handler is called immediately prior to task termination,
-- if the task completed due to abortion.
end Ada.Task_Termination;
!wording
!discussion
Many safety critical and high integrity systems prohibit (or discourage)
exception handling, and so the use of a "when others" handler at the
outer most level of the task body level is then not 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. The proposed solution is applicable
at the general language level but it is also appropriate for a
Ravenscar environment.
The package could be a child of Ada.Task_Identification.
The primary goal of this proposal is to address only the issue of silent
termination. Consequently, the proposal does not address issues of
termination as a result of failures after task creation but before task
activation. It is not clear how useful detection of failure after creation
but before activation is, as the declarative region in which the task is
being created could fail before the creation point. Hence, the order of
elaboration of the declarative region containing the task would dictate
whether or not the event would be detected. Note also, failure of task
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.
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.
If the task has already terminated, the handler is called on the stack
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
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):
T'Unhandled_Exception_Handler
T'Abnormal_Termination_Handler
T'Normal_Termination_Handler
T'Activation_Failure_Handler
T'Termination_Without_Activation_Handler
These would return subprograms similar to that defined above.
They can be set via attribute_definition_clauses. This gives more
functionality than the selected approach (in particular it allows failures
before task activation to be handled). However, it has more impact on
the language definition. The approach was rejected because a) of the
language impact, b) the assumed target market here is a Ravenscar
environment and failures before the tasks begin their execution would
cause the program to fail, and c) the added functionality is of questionable
use.
Another drawback of the attribute approach is that it doesn't scale up to
applications where tasks are allocated dynamically (at least not if the
program needs to associate a different termination hook with different
dynamically allocated tasks of the same task type).
A pragma-based approach has the same set of issues as the attribute
approach.
!example
The following two examples illustrate 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.
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);
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:
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;
!ACATS test
!appendix
At the 11th International Real-Time Ada Workshop, AI95-00266 was discussed.
The following is taken from Ben Brosgol's rapporteur's report.
3.2 AI-266: Task termination procedure
This AI proposes a mechanism ("task group") through which the application
programmer can provide "termination hooks" for procedures that are called
under a variety of circumstances related to task termination (unhandled
exception, abort, normal termination, never activated). The task group
concept is based on the "thread group" notion in Java. The AI stems from a
discussion at the Exceptions workshop at Ada Europe 2001.
Several issues were discussed:
(1) Is "silent task death" a problem that needs to be solved?
(2) Is anything special needed (i.e., can controlled types be used)?
(3) Is the Java thread group an appropriate basis for the design?
Regarding (1), there was general support for the need for such functionality
(e.g. for fault tolerance or "health monitoring"); the vote was 13-0. Note:
this vote applied only to the unhandled exception hook, not to the
additional items (abort etc.), although subsequent discussion showed support
for these also.
Regarding (2), controlled types alone are not sufficient. Finalization for
a controlled object occurs when the object is destroyed, but (e.g. in
Ravenscar) such destruction will only take place when the object is
unchecked-deallocated, and unchecked deallocation might be disallowed by the
implementation or by application requirements. Moreover the "hook" needs to
be invoked not when the object is finalized, but at the earlier point when
the task terminates.
Regarding (3), there was some concern that the design was based on a Java
feature which seems to have fallen into disrepute in Java. Bloch's book
"Effective Java" states: "... thread groups are largely obsolete.... [they]
don't provide much in the way of useful functionality, and much of the
functionality they do provide is flawed. Thread groups are best viewed as
an unsuccessful experiment, and you may simply ignore their existence.... If
you are designing a class that deals with logical groups of threads, just
store the Thread references comprising each logical group in an array or
collection." Bloch goes on to observe: "There is a minor exception to the
advice that you should simply ignore thread groups. One small piece of
functionality is available only in the ThreadGroup API. The
ThreadGroup.uncaughtException method is automatically invoked when a thread
in the group throws an uncaught exception." The AI would need a much
stronger rationale for the introduction of a ThreadGroup-based mechanism,
given this negative experience from Java.
The vote on the specific proposal in the AI was 0-16; it was felt that the
mechanism was complex and beyond the needed functionality, and that it was
stylistically clumsy to use if all you wanted to do was to provide an
unhandled exception procedure for a specific task. Several alternative
approaches were conjectured: special task attribute procedures, and a
proposal based on task-ids.
An example of a possible approach using attributes:
task T; -- also OK for task type
procedure My_Unhandled_Exception_Procedure(Id : Task_Id; E :
Exception_Occurrence);
for T'Unhandled_Exception_Hook use My_Unhandled_Exception_Procedure;
A drawback of using special attributes is that it affects the compiler;
however, the original AI might also affect the compiler in terms of the need
for calls to be inserted at special places. Another drawback of the
attribute approach is that it doesn't scale up to applications where tasks
are allocated dynamically (at least not if the program needs to associate a
different termination hook with different dynamically allocated tasks of the
same task type).
A pragma-based approach has the same set of issues as the attribute
approach.
The AI uses OOP and provides a flexible and extensible framework, whereas
the attribute and task id approaches are somewhat more special purpose.
However, OOP may be a disadvantage in some communities, particularly the
High Integrity domain, so the use of OOP is a double-edged sword.
There was agreement that whatever mechanism was adopted should be applicable
both to the full language and to restricted environments such as Ravenscar.
Subsequent Email exchanges
--------------------------
From: Brian Dobbing
Ben,
Thanks for the copy of the write-up. I have a few observations:
> 3.2 AI-266: Task termination procedure
> Several issues were discussed:
> (1) Is "silent task death" a problem that needs to be solved?
> (2) Is anything special needed (i.e., can controlled types be used)?
> (3) Is the Java thread group an appropriate basis for the design?
> Regarding (2), controlled types alone are not sufficient. Finalization
for
> a controlled object occurs when the object is destroyed, but (e.g. in
> Ravenscar) such destruction will only take place when the object is
> unchecked-deallocated, and unchecked deallocation might be disallowed by
the
> implementation or by application requirements. Moreover the "hook" needs
to
> be invoked not when the object is finalized, but at the earlier point when
> the task terminates.
This argument is invalid. The controlled types solution is based on
declaring
a controlled object *inside* the task body declarative part so that when
the task terminates (by whatever reason) it executes the Finalize routine
of its local controlled object. This works fine in implementation terms.
The counter-argument should be as follows:
(a) The handling of task termination is required in programs that execute
with a restricted run-time system, such as Ravenscar, in which the use of
local controlled objects may be prohibited via the No_Nested_Finalization
restriction.
(b) The controlled object solution relies on the finalization of the object
being the last action of the task. If further controlled objects were
inserted (say during maintenance) earlier in the declarative part, these
would not be finalized prior to the "termination handler" Finalize routine
which may give rise to unpredictable effects.
(c) The controlled object solution is somewhat kludgey and obscures the
readability of the true intent (and hence (b) is more likely to occur).
The handling of task termination is considered sufficiently important by the
real time and high integrity communities as to warrant more "first class"
explicit treatment.
> Regarding (3), there was some concern that the design was based on a Java
> feature which seems to have fallen into disrepute in Java....
This argument is also invalid. It is generally accepted that the *only*
useful use of thread groups in Java is for thread termination, especially
via uncaught exceptions. The Java execution model is therefore likely to
continue to use thread groups in this way. Having an Ada model of task
termination / unhandled exceptions that is compatible with Java's should
simplify mixed-language systems, e.g. running Ada with RT-Java.
> The AI uses OOP and provides a flexible and extensible framework, whereas
> the attribute and task id approaches are somewhat more special purpose.
> However, OOP may be a disadvantage in some communities, particularly the
> High Integrity domain, so the use of OOP is a double-edged sword.
This is also becoming an invalid argument. The use of OOP in real-time and
even high integrity systems is emerging, given certain restrictions. For
example, at Praxis we are currently adding support for a subset of Ada's
OOP features (no class-wide programming, but everything else in) into the
SPARK Examiner because of market pressure. Also the High Integrity Profile
for Java that I prepared for the J Consortium of course supports OOP (it
wouldn't be Java at all if it didn't). I have been to several presentations
of critical real-time systems that are using the OOP features of Ada.
IMHO, I think that the group needs stronger arguments than this to show
that the current ARG solution should be rejected.
-- Brian
>From Ben Brosgol:
> Ben,
>
> Thanks for the copy of the write-up. I have a few observations:
>
> > 3.2 AI-266: Task termination procedure
>
> > Several issues were discussed:
> > (1) Is "silent task death" a problem that needs to be solved?
> > (2) Is anything special needed (i.e., can controlled types be used)?
> > (3) Is the Java thread group an appropriate basis for the design?
>
> > Regarding (2), controlled types alone are not sufficient. Finalization
> for
> > a controlled object occurs when the object is destroyed, but (e.g. in
> > Ravenscar) such destruction will only take place when the object is
> > unchecked-deallocated, and unchecked deallocation might be disallowed by
> the
> > implementation or by application requirements. Moreover the "hook"
needs
> to
> > be invoked not when the object is finalized, but at the earlier point
when
> > the task terminates.
>
> This argument is invalid. The controlled types solution is based on
> declaring
> a controlled object *inside* the task body declarative part so that when
> the task terminates (by whatever reason) it executes the Finalize routine
> of its local controlled object. This works fine in implementation terms.
OK, I was thinking that it was the reverse (declare a task inside a
controlled object). Nonetheless, your counter-arguments are pretty
convincing :-)
> The counter-argument should be as follows:
>
> (a) The handling of task termination is required in programs that execute
> with a restricted run-time system, such as Ravenscar, in which the use of
> local controlled objects may be prohibited via the No_Nested_Finalization
> restriction.
>
> (b) The controlled object solution relies on the finalization of the
object
> being the last action of the task. If further controlled objects were
> inserted (say during maintenance) earlier in the declarative part, these
> would not be finalized prior to the "termination handler" Finalize routine
> which may give rise to unpredictable effects.
>
> (c) The controlled object solution is somewhat kludgey and obscures the
> readability of the true intent (and hence (b) is more likely to occur).
> The handling of task termination is considered sufficiently important by
the
> real time and high integrity communities as to warrant more "first class"
> explicit treatment.
>
> > Regarding (3), there was some concern that the design was based on a
Java
> > feature which seems to have fallen into disrepute in Java....
>
> This argument is also invalid. It is generally accepted that the *only*
> useful use of thread groups in Java is for thread termination, especially
> via uncaught exceptions. The Java execution model is therefore likely to
> continue to use thread groups in this way. Having an Ada model of task
> termination / unhandled exceptions that is compatible with Java's should
> simplify mixed-language systems, e.g. running Ada with RT-Java.
I disagree. The task group / thread group machinery looks like a very heavy
way to achieve a conceptually simple effect, and I don't find the
mixed-language argument convincing. I know the style required in Java to
define an uncaughtException method, and it is rather clumsy. At best,
consistency with Java thread groups is a "nice to have" aspect; at worst, it
will have people wondering why we chose to include a feature that was, in
the words of Josh Bloch (a respected Java expert), a "failed experiment".
> > The AI uses OOP and provides a flexible and extensible framework,
whereas
> > the attribute and task id approaches are somewhat more special purpose.
> > However, OOP may be a disadvantage in some communities, particularly the
> > High Integrity domain, so the use of OOP is a double-edged sword.
>
> This is also becoming an invalid argument. The use of OOP in real-time and
> even high integrity systems is emerging, given certain restrictions. For
> example, at Praxis we are currently adding support for a subset of Ada's
> OOP features (no class-wide programming, but everything else in) into the
> SPARK Examiner because of market pressure. Also the High Integrity Profile
> for Java that I prepared for the J Consortium of course supports OOP (it
> wouldn't be Java at all if it didn't). I have been to several
presentations
> of critical real-time systems that are using the OOP features of Ada.
I'm pleased to see that OOP is making inroads, but I don't see an advantage
in a solution that requires OOP when there are technically acceptable
solutions that do not. Are there potential Ada real-time users who would be
attracted more by an OOP than a non-OOP solution? Perhaps, but I am
skeptical. Are there potential Ada real-time users who would be attracted
more by a non-OOP than an OOP solution? At present, yes.
> IMHO, I think that the group needs stronger arguments than this to show
> that the current ARG solution should be rejected.
This sounds like very strange reasoning. We were given a proposal to
review, there was no one at the IRTAW who was a champion for the AI, and we
provided our reaction. The negative vote was not even close. If the ARG
was trying to get the IRTAW to support the AI, then they didn't quite
achieve this goal :-)
As you have observed, Andy is in the process of preparing an alternative
proposal for task termination hooks. IMHO the way to proceed will be to
have a technical analysis of the two approaches (including examples of
typical usages, analysis of race condition avoidance, etc.) and then make an
appropriate judgement. (The "IMHO" needs to be taken into account. I am
speaking here for myself; perhaps Joyce would like to formulate an official
response on behalf of IRTAW.)
> -- Brian
-Ben
From: Brian Dobbing
Ben,
You wrote:
> at worst, it
> will have people wondering why we chose to include a feature that was, in
> the words of Josh Bloch (a respected Java expert), a "failed experiment".
This observation by Bloch was a (correct) comment on thread groups as
the big picture of a class of operations applicable to all threads,
whereas he does acknowledge the usefulness of ThreadGroup.uncaughtException
I believe (unless I am mistaken??), which remains the standard way to
code centralised fault management for unhandled exceptions, doesn't it?
> I'm pleased to see that OOP is making inroads, but I don't see an
advantage
> in a solution that requires OOP when there are technically acceptable
> solutions that do not....
I basically agree. The point I was making is that rejecting a solution
just because it uses OOP is much less compelling than it used to be. I
actually was the one to propose the Task_IDs model for Ada95 in preference
to task classes on the grounds that it was a non-OOP solution, and won.
But that was then - OOP is more acceptable now, and so an argument to
reject a solution just on these grounds is weak.
-- Brian
****************************************************************
From: Tucker Taft
Sent: Friday, May 31, 2002 2:29 PM
> ... 3.2 AI-266: Task termination procedure
>
> This AI proposes a mechanism ("task group") through which the application
> programmer can provide "termination hooks" for procedures that are called
> under a variety of circumstances related to task termination (unhandled
> exception, abort, normal termination, never activated). The task group
> concept is based on the "thread group" notion in Java. The AI stems from a
> discussion at the Exceptions workshop at Ada Europe 2001.
>
> Several issues were discussed:
> (1) Is "silent task death" a problem that needs to be solved?
> (2) Is anything special needed (i.e., can controlled types be used)?
> (3) Is the Java thread group an appropriate basis for the design?
>
> Regarding (1), there was general support for the need for such functionality
> (e.g. for fault tolerance or "health monitoring"); the vote was 13-0. Note:
> this vote applied only to the unhandled exception hook, not to the
> additional items (abort etc.), although subsequent discussion showed support
> for these also.
>
> Regarding (2), controlled types alone are not sufficient. Finalization for
> a controlled object occurs when the object is destroyed, but (e.g. in
> Ravenscar) such destruction will only take place when the object is
> unchecked-deallocated, and unchecked deallocation might be disallowed by the
> implementation or by application requirements. Moreover the "hook" needs to
> be invoked not when the object is finalized, but at the earlier point when
> the task terminates.
This and other comments indicate that this proposal was
not sufficiently explained. Finalization was used to
reset the current task creation group. This was for
stack-resident objects, not heap-allocated objects. Perhaps
the point is that Ravenscar doesn't allow local finalization.
If that is the case, then clearly this proposal needs to be
rethought. I still believe it is desirable to associate
a handler with all local tasks in a given scope, rather than
having to edit the source code of the task (type) body to specify
how it should handle unhandled exceptions.
If you have to edit the task body, then you might as well
throw in a "when others =>" handler. It may be that
you are using a subset that disallows handlers, but
the solution proposed seems no better than writing
a "when others" handler, and is certainly not something
that someone who could write "when others" would be
interested in.
If the proposal provided is too complicated, I would go
towards providing a single global procedure which is called
when a task dies. A slight enhancement would be to have one
handler per master task. Since the only "master task" in
ravenscar is the environment task, this is equivalent to the
one-global-procedure proposal, but for non-ravenscar environments,
allowing other master tasks to specify a different handler for
their sub tasks seems straightforward and useful.
****************************************************************
From: Randy Brukardt
Sent: Monday, June 10, 2002 10:30 PM
I've just finished posting these AIs, and I see a substantial disconnect here
between the IRTAW11 position and the needs of the rest of Ada users.
The write-up for the alternative proposal gives two alternatives in the
existing Ada 95 language, one involving exception handling, and other involving
finalization. The primary reason for rejecting them is that Ravenscar
implementations might not support them. This isn't a very compelling reason.
When someone cuts off their arms (exception handling) and legs (finalization)
[IMHO the two most important runtime features of Ada], they should not complain
that they cannot get around. It also seems weird that such a system would even
allow an exception to be raised in the normal manner, given that the only
possible results are trouble (if it is in the environment task) and more
trouble (if it is in another task). I would expect any exception to be
immediately fatal.
Moreover, the alternative proposal really doesn't add anything whatsoever
unless you are unwilling or unable to use the existing facilities. That means
it offers next to nothing to the significant number of Ada users that have
tasks in their programs but which are not real-time.
The alternative proposal requires that every task be either modified to call or
visible to the monitoring code. This is a very unrealistic requirement in
non-real-time code. (I'd expect it to be unrealistic for real-time code as
well, but I don't want to claim to speak for the IRTAW.) The original proposal
provides a way to add such a handler to all tasks in the system without
modifying any tasks, or even knowing what they are. This is a much more
practical debugging tool for systems which are constructed from pre-existing
parts.
For example, Claw contains a variety of tasks, all hidden in the bodies of Claw
packages. There purposely is no visibility on these tasks. It would be valuable
to insure that every task would notify a debugging manager if it unexpectedly
disappeared. Certainly, if each task has to be modified to accomplish that, it
is likely that some tasks would be missed or purposely omitted, and in such a
case, Murphy's Law is almost certain to strike.
Moreover, an end user using Claw has no way at all to determine if one of
Claw's tasks has crashed. If the exception handler failed (more on this in a
moment), the program can easily end up deadlocked.
In any case, neither proposal deals with the number one problem with the
existing Ada semantics. Most of the technical support calls that RRS has had
vis-a-vis tasking have had to do with tasks silently disappearing when their
task stack space is exhausted. The standard solution of an "others" handler
does not necessarily work in this case.
task body Tsk is
begin
...
exception
when others =>
Ada.Text_IO.Put_Line ("*** Exception raised in TSK!!!");
end;
What happens frequently is that Storage_Error is raised when Tsk's stack is
exhausted; then the "others" handler is executed, but the call on Text_IO
(which is accomplished by the task, thus using the task's stack) raises
Storage_Error again. This second raising causes the tasks to terminate silently
anyway, and no message is output. The program deadlocks, and the user then
thinks that there is something wrong with the compiler and calls us up.
The alternative proposal (which is exactly this code dressed up as a package)
would obviously suffer from precisely the same problem. The original proposal
has the same problem, requiring that Current_Task be the terminating task.
Again, the call to the termination handler could raise Storage_Error.
In view of this problem, I view both of these proposals as unacceptable. About
all either proposal would accomplish would be to give users a false sense of
security that they would find out about task problems. But in practice, the
most important such problem would continue to go undetected. I would expect
that the number of support calls from this problem would go up, rather than
down, if either of these proposals was adopted.
It would be possible to fix the original proposal to solve this problem, but
any fix would necessarily complicate an already complicated proposal. The
alternative proposal would require many additional capabilities in order to be
worthwhile, including (but not necessarily limited to) handlers for all tasks
in a program, preferably handlers for a subset of tasks (based on elaboration
or masters), and some fix for the Storage_Error problem. I would expect any
such proposal to be too complex for the IRTAW group.
I'm well aware that real-time folks are an important customer for Ada, and I
don't believe that we want to adopt a solution contrary to their interests. I
also do not believe that there is any value to adopting a solution that does
not solve any real problems for other Ada users (especially if it appears to
promise to solve those problems).
Therefore, I believe that further work in this area is a complete waste of
effort. When these proposals come up for consideration at the next meeting, I
intend to introduce a motion classify them both as "No Action".
If you believe otherwise, please make an effort to solve the real problems
here.
Randy Brukardt
(Speaking for himself and for R.R. Software, not as ARG Editor)
****************************************************************