Handle a signal

From Rosetta Code
Task
Handle a signal
You are encouraged to solve this task according to the task description, using any language you may know.

Most general purpose operating systems provide interrupt facilities, sometimes called signals. Unhandled signals generally terminate a program in a disorderly manner. Signal handlers are created so that the program behaves in a well-defined manner upon receipt of a signal.

For this task you will provide a program that displays a single integer on each line of output at the rate of one integer in each half second. Upon receipt of the SigInt signal (often created by the user typing ctrl-C) the program will cease printing integers to its output, print the number of seconds the program has run, and then the program will terminate.

Ada

Signal Handler

Ada signal handlers must be defined at the library level. The following package defines a simple signal handler for the SigInt signal. <lang ada> with Ada.Interrupts; use Ada.Interrupts; with Ada.Interrupts.Names; use Ada.Interrupts.Names;

package Sigint_Handler is

     protected Handler is
     entry Wait;
     procedure Handle;
     pragma Interrupt_Handler(Handle);
     pragma Attach_Handler(Handle, Sigint);
     private
     Call_Count : Natural := 0;
  end Handler;

end Sigint_Handler; </lang> <lang ada> package body Sigint_Handler is

  -------------
  -- Handler --
  -------------
  protected body Handler is
     ----------
     -- Wait --
     ----------
     entry Wait when Call_Count > 0 is
     begin
        Call_Count := Call_Count - 1;
     end Wait;
     ------------
     -- Handle --
     ------------
     procedure Handle is
     begin
        Call_Count := Call_Count + 1;
     end Handle;
  end Handler;

end Sigint_Handler; </lang> A signal may be received at any time in a program. Ada signal handling requires a task to suspend on an entry call for the handler which is executed only when the signal has been received. The following program uses the interrupt handler defined above to deal with receipt of SigInt. <lang ada> with Ada.Calendar; use Ada.Calendar; with Ada.Text_Io; use Ada.Text_Io; with Sigint_Handler; use Sigint_Handler;

procedure Signals is

  task Counter is
     entry Stop;
  end Counter;
  task body Counter is
     Current_Count : Natural := 0;
  begin
     loop
        select
           accept Stop;
           exit;
        or delay 0.5;
        end select;
        Current_Count := Current_Count + 1;
        Put_Line(Natural'Image(Current_Count));
     end loop;
  end Counter;
  task Sig_Handler;
  
  task body Sig_Handler is
     Start_Time : Time := Clock;
     Sig_Time : Time;
  begin
     Handler.Wait;
     Sig_Time := Clock;
     Counter.Stop;
     Put_Line("Program execution took" & Duration'Image(Sig_Time - Start_Time) & " seconds");
  end Sig_Handler;
     

begin

  null;
        

end Signals; </lang>

Sample Output

 1
 2
 3
 4
 5
 6
 7
 8
Program execution took 4.348057086 seconds

Forth

Works with: GNU Forth

Normally Gforth handles most signals (e.g., the user interrupt SIGINT, or the segmentation violation SIGSEGV) by translating it into a Forth THROW.

-28 constant SIGINT

: numbers ( n -- n' )
  begin dup . cr  1+  500 ms again ;

: main
  utime
  0 begin
    ['] numbers catch
    SIGINT =
  until drop
  utime d- dnegate
  <# # # # # # # [char] . hold #s #> type ."  seconds" ;

main bye

Perl

Perl's sleep doesn't handle non-integral arguments correctly on some platforms, so this program prints a number every two seconds.

<lang perl>my $start = time;

$SIG{INT} = sub

  {print 'Ran for ', time - $start, " seconds.\n";
   exit;};

for (my $n = 0 ;; sleep 2)

  {print ++$n, "\n";}</lang>

Python

The following example should work on all platforms. <lang python>import time

def intrptWIN():

  procDone = False
  n = 0
  while not procDone:
     try:
        time.sleep(0.5)
        n += 1
        print n
     except KeyboardInterrupt, e:
        procDone = True

t1 = time.time() intrptWIN() tdelt = time.time() - t1 print 'Program has run for %5.3f seconds.' % tdelt</lang>

There is a signal module in the standard distribution that accomodates the UNIX type signal mechanism. However the pause() mechanism is not implemented on Windows versions. <lang python>import signal, time, threading done = False n = 0

def counter():

  global n, timer
  n += 1
  print n
  timer = threading.Timer(0.5, counter)
  timer.start()

def sigIntHandler(signum, frame):

  global done
  timer.cancel()
  done = True

def intrptUNIX():

  global timer
  signal.signal(signal.SIGINT, sigIntHandler)
  timer = threading.Timer(0.5, counter)
  timer.start()
  while not done:
     signal.pause()

t1 = time.time() intrptUNIX() tdelt = time.time() - t1 print 'Program has run for %5.3f seconds.' % tdelt</lang>

How about this one? It should work on all platforms; and it does show how to install a signal handler: <lang python>import time, signal

class WeAreDoneException(Exception):

   pass

def sigIntHandler(signum, frame):

   signal.signal(signal.SIGINT, signal.SIG_DFL) # resets to default handler
   raise WeAreDoneException

t1 = time.time()

try:

   signal.signal(signal.SIGINT, sigIntHandler)
   n = 0
   while True:
       time.sleep(0.5)
       n += 1
       print n

except WeAreDoneException:

   pass

tdelt = time.time() - t1 print 'Program has run for %5.3f seconds.' % tdelt</lang>

Ruby

<lang ruby>t1 = Time.now

catch :done do

 Signal.trap('INT') do
   Signal.trap('INT', 'DEFAULT') # reset to default
   throw :done
 end
 n = 0
 loop do
   sleep(0.5)
   n += 1
   puts n
 end

end

tdelt = Time.now - t1 puts 'Program has run for %5.3f seconds.' % tdelt</lang>