2011-05-19 15 views
12

Hay algunas llamadas de 'limpieza' que quiero que se llamen cuando se apaga/mata una aplicación Ada.Ada Shut Hook

Por ejemplo, si yo estaba en java, me gustaría hacer algo como esto para lograr el efecto de tener algo que se llama al apagar el equipo:

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

¿Hay algo similar en Ada u otra manera de lograr esto?

+2

su ejemplo de Java solo se invoca cuando se cierra la JVM, no el programa en sí. – oenone

Respuesta

7

Puede crear un objeto Controlado (o Limitado_Controlado) para el procedimiento principal, que llama a las cosas necesarias en su método de Finalización.

Tenga en cuenta que no puede acceder a las variables locales del procedimiento principal, por lo tanto, coloque todo lo necesario en el objeto controlado.

ejemplo:

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; 
+0

¡Agradable! No pensé en eso. Este irá inmediatamente a mi pequeño libro de ideas inteligentes. Gracias. –

+0

No creo que se especifique el orden de las llamadas a Finalize, por lo que no debe depender de eso si usa más de un objeto controlado. – oenone

+0

+1 Este fue mi primer pensamiento también. Me interesaría saber si el código de Finalización se ejecuta cuando los procesos son eliminados por otros procesos (por ejemplo: Unix 'kill -9'). –

2

Parece que debería haber una manera de hacer esto en Ada puro, pero no pude encontrar uno.

Una idea sería utilizar Interfaces.C y llamar al atexit() con una función de devolución de llamada que realiza la limpieza. No lo he intentado, pero no puedo pensar en ninguna razón por la que no funcionaría.

More info on Ada callbacks from C

+0

Correcto, no pudiste encontrar uno. Los tipos controlados o el lanzamiento deliberado de excepciones harán el trabajo por usted. – NWS

+1

Sospecho que el tiempo de ejecución de Ada se hubiera finalizado al salir del programa principal, lo que significa que sería incorrecto llamar al código Ada desde 'atexit()'. –

3

Si se trata de un apagado controlado iniciado por el usuario o debido a que el programa se hace simplemente haciendo lo que hace, a continuación, simplemente puede añadir una última llamada a un procedimiento de limpieza.

Si el programa finaliza debido a una señal de interrupción, como un usuario que envía una señal SIGINT o el sistema se apaga, puede capturar esas señales y poner su procedimiento de limpieza en la devolución de llamada registrada.

Escribí un breve ejemplo sobre cómo detectar interrupciones usando Ada. Está disponible en github y como wiki article.

Otra opción es usar el paquete POSIX de Florist desde libre.adacore.com. Tal vez hay algo útil en el paquete de señales posix.

10

Puesto que un programa principal Ada es tratado como una tarea, puede utilizar el paquete Ada.Task_Termination para gestionar la limpieza posterior a la ejecución. Hay un escrito sobre esto en el Ada 2005 Rationale, y lo que sigue es una demostración rápida que formé y que se basa en el ejemplo.

usted tiene que proporcionar un procedimiento de terminación nivel de la biblioteca protegida, así que aquí tiene un paquete para que:

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; 

cuerpo:

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; 

programa principal (que está establecido para una terminación normal según lo fijado , descomente las dos últimas líneas y ejecútelo para ver el efecto de una terminación desencadenada por excepción no controlada):

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; 
+0

No lo sabía. Las JVM generalmente tienen un hilo de reaper que tiene un propósito similar. – trashgod

+0

@trashgod: De vez en cuando, mientras estoy rozando el índice Ada LRM para algo u otra de las entradas, me llamará la atención. Task_Termination era una de esas cosas, ya que las excepciones no manejadas en tareas que causaban "fallas parciales" era un problema que estaba tratando con un proyecto hace muchos años y me alegré de ver que se había abordado. Al ver esta pregunta, recordé esa capacidad, y entonces investigué si sería una opción viable para limpiar después de la tarea principal del programa. Pensé que debería ser, y voila! Así que fue :-) –

1

Si se permite que el programa se cierre correctamente, puede usar las funciones de idioma estándar como tipos controlados para proporcionar su comportamiento de "apagado".

Si el SO no puede cerrar bien el programa, entonces no existe una forma definida por el idioma para hacerlo en cualquier idioma. Tendrá que usar algún tipo de llamada de sistema operativo para hacerlo.

Tenga en cuenta que el ejemplo que mostró no era una llamada de Java, sino una llamada de JVM. JVM = Java Virtual Machine ... esencialmente el sistema operativo Java. Puede hacer la misma llamada de Ada, suponiendo que su código Ada se ejecuta en la JVM. Si está ejecutando bajo Windows, tendría que hacerlo con llamadas al sistema Win32. Puedes hacer eso desde Ada, pero obviamente las llamadas exactas no están definidas de manera portátil en el idioma.

+0

Excelente punto. Como estudiante tanto de Ada como de Java, aprendí a desconfiar de [falsos cognados] (http://en.wikipedia.org/wiki/False_cognate) en general y de las características extralingüísticas en particular. – trashgod

+0

@trashgod - Sí. Una de las mayores cargas de Ada es que todos los codificadores C parecen pensar que cada llamada de sistema operativo es una "función C", y los programadores Java parecen pensar que toda la JVM es parte del lenguaje Java. –