Ada Programming/Libraries/Ada.Interrupts


This language feature is available from Ada 95 on.

Ada. Time-tested, safe and secure.
Ada. Time-tested, safe and secure.

Ada.Interrupts is a unit of the Predefined Language Environment since Ada 95.

Introduction

edit

Sometimes it's necessary for your program to be able to catch and handle interrupts such as SIGINT, SIGTERM or SIGHUP, and for this we have the Ada.Interrupts package. Attaching handlers to interrupts can be done both statically and dynamically and, luckily, both methods are easy to grasp. The following program will show you both methods.

Example

edit

I've commented the program, so it should be fairly easy to understand what's going on. You can grab the entire program from this Git repository:

 https://github.com/kjseefried/Catch_Signals

Git is available for all Linux distros. The commands to fetch Catch_Signals using Git and then compile it are:

 $ git clone https://github.com/kjseefried/Catch_Signals
 $ cd Catch_Signals
 $ gnatmake -P catch_signals.gpr

The executable can now be found in the exe/ directory.

If you don't feel like using Git, the full source code can be copied from below. I will of course do my best to make sure that the code on this page matches the code in the Git repository, but since this is a wiki where anybody can make changes, I cannot guarantee the safety of running the code on this page, so use it with caution.

The program attach signal handlers to these interrupts:

  • SIGHUP
  • SIGINT
  • SIGPWR
  • SIGTERM

You can send those interrupts to the running program like this:

 kill -SIGHUP pid

Where pid is the Process Id of the program.

catch_signals.gpr

edit

This file sets up compile options. It informs the compiler of the location of the source files and where to put the object and the executable files. You must, of course, create the exe and build directories, if you haven't cloned the Git repository.

catch_signals.gpr

-------------------------------------------------------------------------------
--                                                                           --
--                       Catch_Signals Project File                          --
--                                                                           --
--                     Copyright (C) 2010, Thomas Løcke                      --
--                                                                           --
--  Catch_Signals is free software;  you can  redistribute it  and/or modify --
--  it under terms of the  GNU General Public License as published  by the   --
--  Free Software  Foundation;  either version 2,  or (at your option) any   --
--  later version.  Catch_Signals is distributed in the hope that it will be --
--  useful, but WITHOUT ANY WARRANTY;  without even the  implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        --
--  General Public License for  more details.  You should have  received  a  --
--  copy of the GNU General Public License  distributed with Catch_Signals.  --
--  If not, write  to  the  Free Software Foundation,  51  Franklin  Street, --
--   Fifth  Floor, Boston, MA 02110 - 1301, USA.                             --
--                                                                           --
-------------------------------------------------------------------------------

project Catch_Signals is

   for Source_Dirs use (".",
                        "src");

   for Main use ("catch_signals.adb");

   for Exec_Dir use "exe";

   for Object_Dir use "build";

   package Ide is

      for Compiler_Command ("ada") use "gnatmake";

   end Ide;

   package Compiler is

      Common_Options := ("-gnatwa",
                         "-gnaty3abcdefhiklmnoprstux",
                         "-Wall",
                         "-O2",
                         "-gnat05");

     for Default_Switches ("Ada") use Common_Options;

   end Compiler;

end Catch_Signals;

catch_signals.adb

edit

This is the main program file. Not much happens here. We simply wait for an interrupt at the Process_Control.Wait statement.

-------------------------------------------------------------------------------
--                                                                           --
--                              Catch_Signals                                --
--                                                                           --
--                     Copyright (C) 2010, Thomas Løcke                      --
--                                                                           --
--  Catch_Signals is free software;  you can  redistribute it  and/or modify --
--  it under terms of the  GNU General Public License as published  by the   --
--  Free Software  Foundation;  either version 2,  or (at your option) any   --
--  later version.  Catch_Signals is distributed in the hope that it will be --
--  useful, but WITHOUT ANY WARRANTY;  without even the  implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Gene-  --
--  ral Public License for  more details.  You should have  received  a copy --
--  of the GNU General Public License  distributed with Catch_Signals.  If   --
--  not, write to  the  Free Software Foundation,  51  Franklin  Street,     --
--  Fifth Floor, Boston, MA 02110 - 1301, USA.                               --
--                                                                           --
-------------------------------------------------------------------------------

with Ada.Text_IO;
with Process_Control;

procedure Catch_Signals 
is
   use Ada.Text_IO; 
begin
   Put_Line ("Lets catch some interrupts!");
   
   Process_Control.Wait;
   --  Wait for an interrupt here. The program is "stuck" here until the
   --  Process_State variable is set to Shutdown by one of the registered
   --  interrupt handlers.
   
   Put_Line ("Interrupt caught - shutting down.");
end Catch_Signals;

src/process_control.ads

edit

In this file we define our interrupt handlers. For an example on how to dynamically attach an interrupt handler, take a look at the SIGHUP code.

-------------------------------------------------------------------------------
--                                                                           --
--                              Catch_Signals                                --
--                                                                           --
--                             process_control                               --
--                                                                           --
--                                  SPEC                                     --
--                                                                           --
--                     Copyright (C) 2010, Thomas Løcke                      --
--                                                                           --
--  Catch_Signals is free software;  you can  redistribute it  and/or modify --
--  it under terms of the  GNU General Public License as published  by the   --
--  Free Software  Foundation;  either version 2,  or (at your option) any   --
--  later version.  Catch_Signals is distributed in the hope that it will be --
--  useful, but WITHOUT ANY WARRANTY;  without even the  implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Gene-  --
--  ral Public License for  more details.  You should have  received  a copy --
--  of the GNU General Public License  distributed with Catch_Signals.  If   --
--  not, write to  the  Free Software Foundation,  51  Franklin  Street,     --
--  Fifth Floor, Boston, MA 02110 - 1301, USA.                               --
--                                                                           --
-------------------------------------------------------------------------------

with Ada.Interrupts.Names;
with Ada.Text_IO;

package Process_Control is
   pragma Unreserve_All_Interrupts;
   --  Make sure that GNAT does not handle any interrupts automatically.
   --  As per GNAT 2010, this pragma only affects SIGINT.

   procedure Wait;
   --  Wait until Process_State is Shutdown.
private
   type State is (Running, Shutdown, Stopped);
   --  Define three different states of the running process.
   
   package State_IO is new Ada.Text_IO.Enumeration_IO (State);
   --  Instantiate a package so we can output the value of Process_State using
   --  the familiar Put procedure.

   Wait_Called : Boolean := False;
   --  Set to True when Wait is called the first time.

   protected Controller is
      entry Check;
      --  Check the Process_State is Shutdown, and if so, shut down the process
      
      function Get_State return State;
      --  Return the current Process_State.
      
      procedure Handle_SIGINT;
      procedure Handle_SIGPWR;
      procedure Handle_SIGTERM;
      pragma Attach_Handler (Handle_SIGINT, Ada.Interrupts.Names.SIGINT);
      pragma Attach_Handler (Handle_SIGPWR, Ada.Interrupts.Names.SIGPWR);
      pragma Attach_Handler (Handle_SIGTERM, Ada.Interrupts.Names.SIGTERM);
      --  Statically attach handlers to the SIGINT, SIGPWR, SIGHUP and SIGTERM 
      --  interrupts. 
      
      procedure Handle_SIGHUP_Change_Handler;
      procedure Handle_SIGHUP_Shutdown;
      pragma Interrupt_Handler (Handle_SIGHUP_Change_Handler);
      pragma Interrupt_Handler (Handle_SIGHUP_Shutdown);
      --  Handle_SIGHUP_* can now be dynamically assigned using the
      --  Ada.Interrupts.Attach_Handler procedure.
      
      entry Start;
      --  Set Process_State to Running.
   private
      Process_State : State := Stopped;
      --  What state the process is in.
   end Controller;

end Process_Control;

src/process_control.adb

edit

Pay special attention to the Wait procedure where the SIGHUP interrupt is attached to the Handle_SIGHUP_Change_Handler procedure, and later re-attached to the Handle_SIGHUP_Shutdown procedure. This means that in order to kill the program using SIGHUP, you have to do kill -SIGHUP pid twice.

-------------------------------------------------------------------------------
--                                                                           --
--                               Catch_Signals                               --
--                                                                           --
--                              process_control                              --
--                                                                           --
--                                  BODY                                     --
--                                                                           --
--                     Copyright (C) 2010, Thomas Løcke                      --
--                                                                           --
--  Catch_Signals is free software;  you can  redistribute it  and/or modify --
--  it under terms of the  GNU General Public License as published  by the   --
--  Free Software  Foundation;  either version 2,  or (at your option) any   --
--  later version.  Catch_Signals is distributed in the hope that it will be --
--  useful, but WITHOUT ANY WARRANTY;  without even the  implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Gene-  --
--  ral Public License for  more details.  You should have  received  a copy --
--  of the GNU General Public License  distributed with Catch_Signals.  If   --
--  not, write to  the  Free Software Foundation,  51  Franklin  Street,     --
--  Fifth Floor, Boston, MA 02110 - 1301, USA.                               --
--                                                                           --
-------------------------------------------------------------------------------

with Ada.Interrupts;

package body Process_Control is

   ------------
   --  Wait  --
   ------------

   procedure Wait
   is
      use Ada.Interrupts;
      use Ada.Text_IO;
      use State_IO;
   begin 
      if not Wait_Called then
         Wait_Called := True;
         
         Attach_Handler 
           (New_Handler => Controller.Handle_SIGHUP_Change_Handler'Access,
            Interrupt   => Ada.Interrupts.Names.SIGHUP);
         --  Dynamically attach a handler to the SIGHUP interrupt.
         
         Put ("Wait called. Process_State is ");
         Put (Controller.Get_State);
         New_Line;
         
         Controller.Start;
         Controller.Check;
      end if;
   end Wait;

   ------------------
   --  Controller  --
   ------------------

   protected body Controller is

      -------------
      --  Check  --
      -------------

      entry Check when Process_State = Shutdown
      is
         use Ada.Text_IO;
         use State_IO;
      begin
         Put ("Check called. Process_State is ");
         Put (Controller.Get_State);
         New_Line;
      end Check;

      -----------------
      --  Get_State  --
      -----------------
      
      function Get_State return State
      is
      begin
         return Process_State;
      end Get_State;
      
      ------------------------------------
      --  Handle_SIGHUP_Change_Handler  --
      ------------------------------------

      procedure Handle_SIGHUP_Change_Handler 
      is
         use Ada.Interrupts;
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGHUP_Change_Handler called.");
         Put_Line 
           ("Dynamically changing handler to Handle_SIGHUP_Shutdown.");
         Put_Line ("Next SIGHUP will stop the program.");
         
         Attach_Handler (New_Handler => Handle_SIGHUP_Shutdown'Access,
                         Interrupt   => Ada.Interrupts.Names.SIGHUP);
         --  Dynamically assign a new handler to the SIGHUP interrupt.
      end Handle_SIGHUP_Change_Handler;
      
      ------------------------------
      --  Handle_SIGHUP_Shutdown  --
      ------------------------------
      
      procedure Handle_SIGHUP_Shutdown
      is
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGHUP_Shutdown called.");
         Process_State := Shutdown;
      end Handle_SIGHUP_Shutdown;
      
      ---------------------
      --  Handle_SIGINT  --
      ---------------------

      procedure Handle_SIGINT 
      is
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGINT called.");
         Process_State := Shutdown;
      end Handle_SIGINT;
            
      ---------------------
      --  Handle_SIGPWR  --
      ---------------------

      procedure Handle_SIGPWR 
      is
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGPWR called.");
         Process_State := Shutdown;
      end Handle_SIGPWR;
                  
      ----------------------
      --  Handle_SIGTERM  --
      ----------------------

      procedure Handle_SIGTERM 
      is 
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGTERM called.");
         Process_State := Shutdown;
      end Handle_SIGTERM;

      -------------
      --  Start  --
      -------------

      entry Start when Process_State = Stopped
      is
         use Ada.Text_IO;
         use State_IO;
      begin
         Process_State := Running;
         Put ("Start called. Process_State is ");
         Put (Controller.Get_State);
         New_Line;
         
         Put_Line ("I will react on SIGHUP, SIGINT, SIGPWR and SIGTERM.");

      end Start;
   end Controller;
end Process_Control;

Specification

edit
--                     Standard Ada library specification
--   Copyright (c) 2003-2018 Maxim Reznik <reznikmm@gmail.com>
--   Copyright (c) 2004-2016 AXE Consultants
--   Copyright (c) 2004, 2005, 2006 Ada-Europe
--   Copyright (c) 2000 The MITRE Corporation, Inc.
--   Copyright (c) 1992, 1993, 1994, 1995 Intermetrics, Inc.
--   SPDX-License-Identifier: BSD-3-Clause and LicenseRef-AdaReferenceManual
-- -------------------------------------------------------------------------

with System;

package Ada.Interrupts is

   type Interrupt_ID is (Implementation_Defined);

   type Parameterless_Handler is access protected procedure;

   function Is_Reserved (Interrupt : in Interrupt_ID) return Boolean;

   function Is_Attached (Interrupt : in Interrupt_ID) return Boolean;

   function Current_Handler (Interrupt : in Interrupt_ID)
     return Parameterless_Handler;

   procedure Attach_Handler (New_Handler : in Parameterless_Handler;
                             Interrupt   : in Interrupt_ID);

   procedure Exchange_Handler (Old_Handler :    out Parameterless_Handler;
                               New_Handler : in     Parameterless_Handler;
                               Interrupt   : in     Interrupt_ID);

   procedure Detach_Handler (Interrupt : in Interrupt_ID);

   function Reference (Interrupt : in Interrupt_ID) return System.Address;

private

end Ada.Interrupts;

See also

edit

Wikibook

edit

External examples

edit

Ada Reference Manual

edit

Ada 95

edit

Ada 2005

edit

Ada 2012

edit

Open-Source Implementations

edit

FSF GNAT

drake