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
C
Standard C's sleep() only provides one-second resolution, so the POSIX usleep() function is used here. (POSIX is not needed for the actual signal handling part.) <lang C>#include <stdio.h>
- include <stdlib.h> // for exit()
- include <signal.h>
- include <time.h> // for clock()
- include <unistd.h> // for POSIX usleep()
static clock_t startTime;
void handleSigint() {
clock_t endTime = clock(); double td = (endTime - startTime) / (double)CLOCKS_PER_SEC; printf("Program has run for %5.3f seconds\n", td); exit(0);
}
int main() {
startTime = clock(); signal(SIGINT, handleSigint); int i=0; for (;;) {
usleep(500000); printf("%d\n", ++i);
} return 0;
} </lang>
Sample Output
1 2 3 Program has run for 1.953 seconds
C#
Signals in C# are called events, and are handled by attaching event handler functions to the event, which are called when the event is triggered.
<lang csharp> using System; //DateTime, Console, Environment classes class Program {
static DateTime start; static void Main(string[] args) { start = DateTime.Now; //Add event handler for Ctrl+C command Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress); int counter = 0; while (true) { Console.WriteLine(++counter); System.Threading.Thread.Sleep(500); } } static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { var end = DateTime.Now; Console.WriteLine("This program ran for {0:000.000} seconds.", (end - start).TotalMilliseconds / 1000); Environment.Exit(0); }
} </lang>
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
Haskell
<lang haskell>import Prelude hiding (catch) import Control.Exception (catch, throwIO, AsyncException(UserInterrupt)) import Data.Time.Clock (getCurrentTime, diffUTCTime) import Control.Concurrent (threadDelay)
main = do t0 <- getCurrentTime
catch (loop 0) (\e -> if e == UserInterrupt then do t1 <- getCurrentTime putStrLn ("\nTime: " ++ show (diffUTCTime t1 t0)) else throwIO e)
loop i = do print i
threadDelay 500000 {- µs -} loop (i + 1)</lang>
OCaml
OCaml's Unix.sleep doesn't handle non-integral arguments, so this program prints a number every second.
<lang ocaml>#load "unix.cma";; (* for sleep and gettimeofday; not needed for the signals stuff per se *)
let start = Unix.gettimeofday ();;
Sys.set_signal Sys.sigint
(Sys.Signal_handle (fun _signum -> Printf.printf "Ran for %f seconds.\n" (Unix.gettimeofday () -. start); exit 0));;
let rec loop n =
Printf.printf "%d\n%!" n; Unix.sleep 1; loop (n + 1)
in
loop 1;;</lang>
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>
PHP
<lang php><?php declare(ticks = 1);
$start = microtime(YES);
function mySigHandler() {
global $start; $elapsed = microtime(YES) - $start; echo "Ran for $elapsed seconds.\n"; exit();
}
pcntl_signal(SIGINT, 'mySigHandler');
for ($n = 0; ; usleep(500000)) // 0.5 seconds
echo ++$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>
Tcl
Core Tcl does not have signal handling. However the
and
extensions do:
Using Expect: <lang tcl>package require Expect
proc sigint_handler {} {
puts "elapsed time: [expr {[clock seconds] - $::start_time}] seconds" set ::looping false
}
trap sigint_handler SIGINT
set start_time [clock seconds] set n 0 set looping true while {$looping} {
puts [incr n] after 500
}</lang>
Similarly, with TclX: <lang tcl>package require Tclx
proc sigint_handler {} {
puts "elapsed time: [expr {[clock seconds] - $::start_time}] seconds" set ::looping false
}
signal trap sigint sigint_handler
set start_time [clock seconds] set n 0 set looping true while {$looping} {
puts [incr n] after 500
}</lang>
With TclX, you don't have to trap signals, you can convert the signal into a catchable error: <lang tcl>package require Tclx
signal error sigint
set start_time [clock seconds] set n 0 proc infinite_loop {} {
while 1 { puts [incr n] after 500 }
} if {[catch infinite_loop out] != 0} {
lassign $::errorCode class name msg if {$class eq "POSIX" && $name eq "SIG" && $msg eq "SIGINT"} { puts "elapsed time: [expr {[clock seconds] - $start_time}] seconds" } else { puts "infinite loop interrupted, but not on SIGINT: $::errorInfo" }
}</lang> With Tcl 8.6, that would be written as: <lang tcl>package require Tclx
signal error sigint
set start_time [clock seconds] proc infinite_loop {} {
while 1 { puts [incr n] after 500 }
} try {
infinite_loop
} trap {POSIX SIG SIGINT} {} {
puts "elapsed time: [expr {[clock seconds] - $start_time}] seconds"
}</lang> Note also that from 8.5 onwards, Tcl also has other mechanisms for delivering interrupt-like things, such as interpreter resource limits which permit stopping an execution after a set amount of time and returning control to a supervisor module. However, this is not driven by user interrupts and is so only tangential to this task.