Version 1.1 of acs/ac-00044.txt
!standard 11.4.1(00) 02-08-28 AC95-00044/01
!class amendment 02-07-13
!status received no action 02-07-13
!subject Addition of Current_Exception function to Ada.Exceptions package
!topic Addition of Current_Exception function to Ada.Exceptions package
!reference RM95-11.4.1 RM95-7.6.1
!from Chris Miller 02-07-10
!keywords Current_Exception, Finalization
There is no way within a finalization routine to find if (a) the
is being called due to the raising of an exception and, if so, (b) the
details of the current exception occurrence.
It is proposed that a new "current_exception" function be added to the
Ada.Exceptions package. Also, the existing
constant should be made aliased.
The function would have the following specification
package Ada.Exceptions is
function Current_Exception return Exception_Occurrence_Access;
Null_Occurrence : aliased constant Exception_Occurrence;
The new function would return either a (pointer to) the current
occurrence or a (pointer to) the Null_Occurrence constant if there is
The following example (see below) shows how this function could be used
in a finalize procedure. It has been compiled and run under GNAT 3.14p
on a PC.
Here are some notes on the example :-
1. The new function and aliased constant have been added to a package called
"Ada1".Exceptions. This is just to avoid some GNAT warnings. In practice
it would be included into the existing Ada.Exceptions package.
2. The program demonstrates a cut down version of a common embedded application.
There is a schedular program (schedular.ads / .adb) that loops "forever".
The program implements events by calling an application procedure P1.proc1.
In this example there is only a single event.
P1.Proc1 is a hierarchy, it calls P2.Proc2 which in turn calls P3.Proc3.
P2.Proc2 contains a controlled variable derived from the Sampled_Controlled
package. A finalize procedure has been specified for this.
P3.Proc3 raises various exceptions each time it is called. It also
simulates part of the operation of the current_exception function by
saving the latest exception_occurrence in a global location. In the
example this is in a global variable. In practice it would likely be
in the task control block of the current task but in any case it would be
hidden from direct access and so the implementation details are not
3. The semantics of the program are such that it should never terminate due
to the raising of a normal exception in an application event. Rather the
exception should be logged and the next event should be delivered, and
so on. However assume that there is a user defined exception called
"fatal_error" (in exception_names.ads) which, when raised by application
code, causes the schedular to terminate in an orderly manner and
(for example) switch processing to a warm standby alternate CPU.
4. Within the finalize routine called in P2.Proc2 there is a need to know
what has caused it to be invoked.
If no exception has been raised, or if any exception except fatal_error
has been raised, then we wish to clean up in the normal manner. This
is to ensure that the system can continue to operate.
However if fatal_error has been raised then the image is going to be
terminated anyway and so cleaning up is a waste of time. Instead we
wish to return control to the scheduler as soon as possible. Calling
an operating system "exit" routine is not desired as this terminates
the image immediately. Rather we require that control should pass back to
the scheduler so that it can terminate the image in a controlled manner.
5. The sample code (in sample_controlled.adb) shows how the proposed
current_exception function is used to determine what, if any, exception
has been raised. Different processing options are then possible.
6. In the case of a fatal_error exception being raised, control returns to the
scheduler, it exits the Main_loop and arranges for a graceful transition
to a backup CPU.
Sample code follows
procedure Main is
package Scheduler is
package body Scheduler is
procedure Start is
while true loop
-- Just to make the demo work.
Exception_Names.Latest_Exception := Ada1.Exceptions.
-- Deliver an "event".
Ada.Text_Io.Put_Line("Scheduler : Event delivered OK.");
when Exception_Names.Fatal_Error =>
Ada.Text_Io.Put_Line("Scheduler : Fatal_Error raised, shutting down ...");
when E: others =>
-- Perform some logging and / or cleanup, but continue processing.
Ada.Text_Io.Put_Line("Scheduler : Exception " & Ada.Exceptions.Exception_Name(E) &
" raised. Continuing ...");
end loop Main_Loop;
-- Perform required shutdown code, e.g. switch to an alternate CPU.
-- Had exit been called in the Finalize procedure then this would
-- not be possible.
Ada.Text_IO.Put_Line("Scheduler : switching to backup CPU ...");
package Exception_Names is
Fatal_Error, Non_Fatal_Error : exception;
-- Simulate the data required for the proposed Current_Exception
-- function by storing the latest exception occurrence (or a pointer to
-- it) in a global location. Note, would not be done this way in practice.
Latest_Exception : Ada.Exceptions.Exception_Occurrence_Access;
package Ada1 is
-- Dummy parent package for Ada1 tree.
Package Ada1.Exceptions is
-- Proposed new function. In practice this would be placed into
-- the *existing* Ada.Exceptions package. Here it has been put
-- into a separate "Ada1".Exceptions, just to get it to compile
-- without warnings.
function Current_Exception return Ada.Exceptions.
-- Existing Null_Occurrence constant is not aliased. Propose that it be
-- made so. For the demo, just define another constant that is aliased
-- so that can have pointers to it.
Aliased_Null_Occurrence : aliased constant Ada.Exceptions.Exception_Occurrence_Access :=
Ada.Exceptions.Save_Occurrence(Source => Ada.Exceptions.Null_Occurrence);
package body Ada1.Exceptions is
-- This is the new proposed function.
return Ada.Exceptions.Exception_Occurrence_Access is
-- This data would normally be hidden from the user. However
-- for this demo program we have just used a global variable.
package P1 is
package body P1 is
procedure Proc1 is
package P2 is
package body P2 is
procedure Proc2 is
C : Sample_Controlled.My_Controlled;
-- Finalize called when C goes out of scope.
package P3 is
package body P3 is
Count : Integer := 0;
procedure Proc3 is
Count := Count + 1;
-- Simulate some possible error conditions.
if count = 1 then
Ada.Text_IO.Put_Line("Proc 3. Doing normal processing");
elsif count = 2 then
Ada.Text_IO.Put_Line("Proc 3. Raising Constraint_Error");
elsif count = 3 then
Ada.Text_IO.Put_Line("Proc 3. Raising Non_Fatal_Error");
elsif count = 4 then
Ada.Text_IO.Put_Line("Proc 3. Raising Fatal_Error");
-- *Simulate* the proposed current_exception function by saving the
-- latest exception occurrence in a global location (In practice this
-- data would of course not be visible to application code, most likely
-- would store it in the task control block of the current task).
-- Then re raise the same exception.
when E : others =>
Exception_Names.Latest_Exception := Ada.Exceptions.Save_Occurrence(E);
Package Sample_Controlled is
type My_Controlled is new Ada.Finalization.Controlled with private;
procedure Finalize (Object : in out My_Controlled);
type My_Controlled is new Ada.Finalization.Controlled with null
package body Sample_Controlled is
procedure Finalize (Object : in out My_Controlled) is
A : Ada.Exceptions.Exception_Occurrence_Access;
-- Finalise routine for the controlled object. We assume that if any exception,
-- excluding Exception_Names.Fatal_Error, is raised then we need to do some
-- cleanup for the controlled object. This may take some time.
-- However if the Fatal_Error exception is raised then we wish to skip the
-- cleanup and return as soon as possible to the scheduler. For example, the
-- scheduler may wish to return control to the operating system and / or
-- switch control to an alternate processor. Wish to have this happen as
-- soon as possible. In this case doing finalization is of no use, since the
-- image is going to be terminated anyway.
-- To distinguish between these two cases we use the proposed current_exception
-- function. This returns the exception occurrence (or a pointer to it) of the
-- exception being handled. If no exception is being raised then a (pointer to)
-- Null_Occurrence is returned. As the existing Null_Occurrence constant is not
-- aliased we have had to fudge this by taking a copy of it.
-- Calling an Operating System exit operation is not what is required. Instead
-- we wish to return control to the scheduler and let it shut down the system
-- in a controlled manner. If exit is called then this is not possible.
A := Ada1.Exceptions.Current_Exception;
if A /= Ada1.Exceptions.Aliased_Null_Occurrence then
if Ada.Exceptions.Exception_Identity(A.all) = Exception_Names.Fatal_Error'Identity then
Ada.Text_Io.Put_Line ("Finalize : Fatal_Error, returning to exec ASAP");
-- And so the exception just propagates. (Don't reraise it or you get
-- Program_Error, ARM 7.6.1(14)).
-- Perform normal finalization for Object, may take some time.
Ada.Text_Io.Put_Line("Finalize : Doing normal cleanup");
-- ... etc.
Output from program
Proc 3. Doing normal processing
Scheduler : Event delivered OK.
Proc 3. Raising Constraint_Error
Finalize : Doing normal cleanup
Scheduler : Exception CONSTRAINT_ERROR raised. Continuing ...
Proc 3. Raising Non_Fatal_Error
Finalize : Doing normal cleanup
Scheduler : Exception EXCEPTION_NAMES.NON_FATAL_ERROR raised. Continuing
Proc 3. Raising Fatal_Error
Finalize : Fatal_Error, returning to exec ASAP
Scheduler : Fatal_Error raised, shutting down ...
Scheduler : switching to backup CPU ...
Consider a typical embedded application where there is a cyclic scheduler
that continually delivers events to application code. An event is
implemented as a procedure in a package. During the event, one or more
subprograms are called, control then returns to the scheduler
and it then selects and delivers the next event. This goes on "forever".
Assume that during the processing of an event application code declares
controlled variable(s). When these go out of scope their finalize procedures
are called in the usual way. Within these finalise procedures it is desired
to know if the routine is being called as a result of an exception being
raised (and if so which one) or as a result of a normal_completion
(ARM 7.6.1 (2)). The application may wish to change the processing done
depending upon which case has caused the finalize to be called.
For example, if there is some fatal error then there may be a need to
shut down the application as soon as possible and transfer processing to
another CPU. Hence doing finalization here may be a waste of time as
the image is going to be shut down anyway.
The general principle is that an application should be able to determine
the circumstances that have caused the finalization procedure to be called
and so adjust processing accordingly.
The proposed current_exception function could be used to do this. Calling
it would return (a pointer to) the current exception_occurrence. From
this details about the exception can be determined as required. In the case
of a normal_completion a (pointer to) the existing Null_Occurrence constant
would be returned.
For the case of abnormal completion due to a task abort (ARM 7.6.1 (2))
the current_exception call would also return Null_Occurrence. There may
be other possibilities here. May even wish to have a separate means of
determining if finalize was called due to an abort but this is probably
another topic that is best left to the language lawyers.
If there are any other cases (??) where it is not possible to access a
valid current occurrence then Null_Occurrence could also be returned.
These would need to be documented.
The current_exception routine would be a new function and would have no
impact on existing code. Making the existing Null_Occurrence constant
aliased should also have no impact.
In many ways the function is analogous to the existing current_task
There should be a new "current_exception" function added to the Ada.Exceptions
package. Also, the existing Ada.Exceptions.Null_Occurrence constant should be
Questions? Ask the ACAA Technical Agent