阿达关闭挂钩

12

当Ada应用程序关闭/终止时,我希望调用一些“清理”函数。

例如,如果我在Java中,我会像这样做来实现在关闭时调用某些内容的效果:

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

在Ada中是否有类似的东西或者其他方法可以实现这个功能?


2
你的 Java 例子只在 JVM 关闭时调用,而不是程序本身。 - Rommudoh
6个回答

12

由于Ada主程序被视为任务,因此您可以使用Ada.Task_Termination包来管理执行后的清理。在Ada 2005 Rationale中有关于此的介绍,以下是我制作的一个快速演示,以构建示例。

您需要提供一个库级受保护的终止过程,因此这里有一个相应的包:

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;

正文:

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;

主程序(默认设置为正常终止,取消最后两行的注释并运行它,以查看未处理异常触发的终止效果):

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;

我不知道。JVM通常有一个收割线程,起着类似的作用。 - trashgod
@trashgod:每当我浏览Ada LRM索引寻找某些东西时,其中的一些条目会引起我的注意。Task_Termination就是其中之一,因为许多年前我在处理项目中出现的任务中未处理的异常导致“部分崩溃”的问题,我很高兴看到它已经得到解决。看到这个问题,我想起了这个功能,所以研究了一下它是否是清理主程序任务后的可行选项。我认为应该可以,然后就成功了! - Marc C

9
您可以创建一个受控(或有限受控)对象到主过程中,在其Finalization方法中调用必要的内容。
请注意,您无法访问主过程的任何局部变量,因此请将任何必要的内容放入受控对象中。
例如:
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;

不错!我没有想到那个。这个想法会立刻被记录在我的聪明点子小本本上。谢谢。 - Thomas Løcke
我认为调用Finalize的顺序未指定,所以如果您使用多个Controlled对象,就不应该依赖于它。 - Rommudoh
+1 这也是我的第一反应。不过我很想知道当进程被其他进程(例如:Unix kill -9)杀死时,Finalization 代码是否会运行。 - T.E.D.
不,如果未捕获的信号导致操作系统终止程序,则无法防止其发生。对于这些情况,请添加信号处理程序。 - Rommudoh

3
如果是用户发起的受控关机或程序完成其任务,那么你可以简单地添加一个最终调用清理过程的语句。
如果程序由于中断信号(例如用户发送SIGINT信号或系统关闭)而终止,则可以捕获这些信号并将清理过程放在注册的回调中。
我写了一个短小的例子来展示如何使用Ada捕获中断。它可以在githubwiki article上找到。
另一种选择是使用libre.adacore.com的Florist POSIX包。也许在posix-signals包中有可用的内容。

2

2
似乎应该有一种纯Ada的方法来做到这一点,但我找不到。一个想法是使用Interfaces.C,并调用atexit(),并使用一个清理回调函数。我没有尝试过,但我想不出任何原因它不会起作用。有关从C中使用Ada回调的更多信息

正确,你找不到一个。控制类型或故意抛出异常将为您完成工作。 - NWS
1
我怀疑当主程序退出时,Ada运行时将被最终化,这意味着从atexit()调用Ada代码是不正确的。 - Simon Wright

1
如果程序被允许正常关闭,则可以使用控制类型等标准语言设施来提供“关闭时”的行为。
如果操作系统不允许程序正常关闭,则没有任何语言定义的方法可以在任何语言中完成。您需要使用某种操作系统调用来完成它。
请注意,您展示的示例不是Java调用,而是JVM调用。JVM = Java虚拟机...基本上是Java操作系统。如果您的Ada代码在JVM上运行,则可以从Ada进行完全相同的调用。如果您在Windows下运行,则必须使用Win32系统调用完成它。您可以从Ada进行这些调用,但显然在语言中没有明确定义确切的调用。

非常好的观点。作为Ada和Java的学生,我已经学会了对于一般的假朋友词以及特别是额外语言特征要保持警惕。 - trashgod
@trashgod - 是的。Ada语言面临的最大问题之一就是所有C语言程序员似乎都认为每个操作系统调用都是“C函数”,而Java编程人员似乎认为整个JVM都是Java语言的一部分。 - T.E.D.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接