12

There are some ‘cleanup’ calls that I want called when an Ada application is shutdown/killed.

For instance if I was in java, I would do something like this to achieve the effect of having something called at shutdown:

Runtime.getRuntime().addShutdownHook(new Thread(){
       public void run(){
            method();
       }
});

Is there anything similar in Ada or another way to achieve this?

mainstringargs
  • 13,563
  • 35
  • 109
  • 174

6 Answers6

12

Since an Ada main program is treated as a task, you can use the Ada.Task_Termination package to manage post-execution cleanup. There's a writeup on this in the Ada 2005 Rationale, and what follows is a quick demo I put together that builds on the example.

You have to provide a library level protected termination procedure, so here's a package for that:

with Ada.Task_Termination;
with Ada.Task_Identification;
with Ada.Exceptions;

package Main_Program_Finalization is

   protected Shutdown_Handler is

      procedure Termination_Finalizer 
        (Cause : in Ada.Task_Termination.Cause_Of_Termination;
         T     : in Ada.Task_Identification.Task_Id;
         X     : in Ada.Exceptions.Exception_Occurrence);
   end Shutdown_Handler;

end Main_Program_Finalization;

Body:

with Text_IO; use Text_IO;

package body Main_Program_Finalization is

   protected body Shutdown_Handler is

      procedure Termination_Finalizer
        (Cause : in Ada.Task_Termination.Cause_Of_Termination;
         T     : in Ada.Task_Identification.Task_Id;
         X     : in Ada.Exceptions.Exception_Occurrence)
      is
         use Ada.Task_Termination;
         use Ada.Task_Identification;
         use Ada.Exceptions;
      begin
         New_Line;
         Put_Line("Shutdown information:");
         New_Line;
         case Cause is
         when Normal =>
            Put_Line("Normal, boring termination");
         when Abnormal =>
            Put_Line("Something nasty happened to task ");
            Put_Line(Image(T));
         when Unhandled_Exception =>
            Put_Line("Unhandled exception occurred in task ");
            Put_Line(Image(T));
            Put_Line(Exception_Information(X));
         end case;
      end Termination_Finalizer;

   end Shutdown_Handler;

end Main_Program_Finalization;

Main program (it's set up for a normal termination as posted, uncomment the last two lines and run it to see the effect of an unhandled-exception triggered termination):

with Main_Program_Finalization;
with Ada.Task_Identification;
with Ada.Task_Termination;
with Text_IO; use Text_IO;

procedure task_term is

   use Ada;

   Task_ID : Task_Identification.Task_Id
     := Task_Identification.Current_Task;

begin
   Put_Line("Main Task ID: " & Task_Identification.Image(Task_ID));

   Put_Line("Setting termination finalizer");
   Task_Termination.Set_Specific_Handler
     (Task_ID, 
      Main_Program_Finalization.Shutdown_Handler.Termination_Finalizer'Access);
   Put_Line("Go off and do things now...");
   delay 1.0;
   Put_Line("Done with mainline processing, the shutdown handler should now execute");

--     Put_Line("Raise an unhandled exception and see what the shutdown handler does");
--     raise Constraint_Error;
end Task_Term;
Marc C
  • 8,664
  • 1
  • 24
  • 29
  • I didn't know that. JVM's typically have a reaper thread that serves a similar purpose. – trashgod May 19 '11 at 16:20
  • @trashgod: Every now and then as I'm skimming the Ada LRM index for something or other one of the entries will catch my eye. Task_Termination was one of those things, since unhandled exceptions in tasks causing "partial crashes" was an issue I was dealing with on a project many years ago and I was glad to see that it had been addressed. On seeing this question I remembered that capability, and so looked into whether it would be a viable option for cleaning up after the main program task. Figured it should be, and voila! So it was :-) – Marc C May 19 '11 at 16:43
9

You could create a Controlled (or Limited_Controlled) object to the main procedure, which calls the necessary stuff in its Finalization method.

Be aware that you can't access any local variables of the main procedure, so put anything necessary into the controlled object.

example:

with Ada.Text_IO;
with Ada.Finalization;
procedure Main is
   type Cleaner is new Ada.Finalization.Limited_Controlled with record
      Some_Interesting_Data : Integer;
   end record;
   overriding procedure Finalize (X : in out Cleaner) is
   begin
      Ada.Text_IO.Put_Line ("Cleaning..." & Integer'Image (X.Some_Interesting_Data));
   end Finalize;
   The_Cleaner : Cleaner;
begin
   Ada.Text_IO.Put_Line ("Main Procedure.");
   The_Cleaner.Some_Interesting_Data := 42;
   Ada.Text_IO.Put_Line ("Finished.");
end Main;
Rommudoh
  • 1,844
  • 10
  • 11
  • Nice! I did not think about that. This one will immediately go into my little book of clever ideas. Thanks. – Thomas Løcke May 19 '11 at 07:18
  • I don't think the order of calls to Finalize are specified, so you shouldn't depend on that if you use more than one Controlled object. – Rommudoh May 19 '11 at 10:51
  • +1 This was my first thought too. I'd be interested to know if Finalization code gets run when processes are killed by other processes (eg: Unix `kill -9`) though. – T.E.D. May 19 '11 at 13:17
  • No, if uncaught signals cause the operating system to terminate the program, it can't do anything against it. For those cases add signal handlers. – Rommudoh May 20 '11 at 06:22
3

If it's a controlled shutdown initiated by the user or because the program is simply done doing what it does, then you can simply add a final call to a cleanup procedure.

If the program is terminated due to an interrupt signal, such as a user sending a SIGINT signal, or the system shutting down, then you can catch those signals and put your cleanup procedure in the registered callback.

I wrote a short example on how to catch interrupts using Ada. It's available at github and as a wiki article.

Another option is using the Florist POSIX package from libre.adacore.com. Perhaps there's something of use in the posix-signals package.

Thomas Løcke
  • 605
  • 4
  • 8
2

You can raise an exception, which can be one defined by the language or one defined in your program. Your exception handler would then execute.

You can also use Ada.Command_Line.Set_Exit_Status to return a code to the invoking environment.

Addendum: You can also handle external interrupts, as shown here.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
2

Seems like there should be a way to do this in pure Ada, but I couldn't find one.

One idea would be to use Interfaces.C and call atexit() with a callback function that does the cleanup. I haven't tried it, but I can't think of any reason it wouldn't work.

More info on Ada callbacks from C

Jeremiah
  • 1,437
  • 8
  • 17
  • Correct, you couldnt find one. Controlled types or deliberate exception throwing will do the job for you. – NWS May 19 '11 at 08:58
  • 1
    I suspect that the Ada runtime would have been finalized on exit from the main program, meaning that it would be incorrect to call Ada code from `atexit()`. – Simon Wright May 19 '11 at 14:24
1

If the program is allowed to shut down nicely, then you can use standard language facilities like controlled types to provide your "on shutdown" behavior.

If the program isn't allowed to shut down nicely by the OS, then there is no language-defined way to do it in any language. You will have to use some kind of OS call to do it.

Note that the example you showed was not a Java call, but a JVM call. JVM = Java Virtual Machine...essentially the Java OS. You could make the exact same call from Ada, assuming your Ada code is running on the JVM. If you are running under Windows instead you'd have to do it with Win32 system calls. You can make those from Ada, but obviously the exact calls aren't portably defined in the language.

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
  • Excellent point. As a student of both Ada and Java, I've learned to be wary of [false cognates](http://en.wikipedia.org/wiki/False_cognate) in general and extra-linguistic features in particular. – trashgod May 19 '11 at 16:11
  • @trashgod - Yeah. One of Ada's biggest burdens is that all C coders seem to think every single OS call is a "C function", and Java coders seem to think that the entire JVM is part of the Java language. – T.E.D. May 19 '11 at 17:48