Merge: Basename fix
[nit.git] / lib / signals.nit
index 88e5950..440fd39 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Module to manage standard C signals
+# ANSI C signal handling
+#
+# Common usage imply 5 steps:
+#
+# 1. Implement the `SignalHandler` interface
+# 2. `redef receive_signal_unsafe` to handle `sigsegv`
+# 3. `redef receive_signal` to handle other signals safely
+# 4, Notify what signals to handle with `handle_signal`
+# 5. If using the safe handler method, routinely call `check_signals`
+#
+# Usage example:
+#
+# ~~~~
+# class MyReceiver
+#      super SignalHandler
+#
+#      redef fun receive_signal(signal)
+#      do
+#              print "received safely {signal}"
+#              if signal == sigalarm then print "Alarm!"
+#      end
+#      redef fun receive_signal_unsafe( signal ) do print "received unsafely {signal}"
+# end
+#
+# var r = new MyReceiver
+#
+# # Handle `sigsegv` signal unsafely (the only way for this one)
+# r.handle_signal(sigsegv, false)
+#
+# # Handle `sigint` and `sigalarm` safely
+# r.handle_signal(sigint, true)
+# r.handle_signal(sigalarm, true)
+#
+# # Ask system to receive a `sigalarm` signal in 1 second
+# set_alarm(1)
+#
+# loop
+#      # Check signals and callback `receive_signal`
+#      var hit = check_signals
+#
+#      if hit then break
+# end
+# ~~~~
 module signals
 
 `{
@@ -22,6 +64,11 @@ module signals
        #define _POSIX_SOURCE 1
        #include <signal.h>
        #include <stdio.h>
+       #include <unistd.h>
+
+       /*
+       */
+       void (*nit_SignalHandler_receive_signal)(void* self, long signal);
 
        /*
                Structure to manage each possible signals
@@ -30,9 +77,9 @@ module signals
        */
        struct nit_signals_ent {
                char raised; /* !=0 if has been raised */
-               SignalHandler handler; /* instance to receive call */
+               void* handler; /* instance to receive call */
                char safely; /* if !=0 then manage signal safely, otherwise react when raised */
-       } nit_signals_list[32] = {0x0};
+       } nit_signals_list[32] = {{0}};
 
        /* Receiver to all signals
                If unsafe, it calls directly the Nit receiver,
@@ -45,7 +92,7 @@ module signals
                        if (nit_signals_list[sig].safely) {
                                nit_signals_list[sig].raised += 1;
                        } else {
-                               SignalHandler_receive_signal(nit_signals_list[sig].handler, sig);
+                               nit_SignalHandler_receive_signal(nit_signals_list[sig].handler, sig);
                        }
                }
        }
@@ -53,21 +100,48 @@ module signals
 
 # Receives the callback from system when a given signal arise
 interface SignalHandler
-       # Called on any signal received
-       fun receive_signal(signal: Int) `{
-       `}
+       # Invoked after a call to `check_signals` if a signal has been raised
+       # (should be redefed by subclasses)
+       #
+       # Should be used by most signals except `sigkill` and `sigstop` since they
+       # cannot be caught, blocked or ignored.
+       #
+       #     class MyReceiver
+       #         super SignalHandler
+       #
+       #         redef fun receive_signal(signal) do print "received safely {signal}"
+       #     end
+       #
+       #     var r = new MyReceiver
+       #     r.handle_signal(sigint, true) # will call back when "check_signals" is called
+       #     # ...
+       #     check_signals # if a signal was received, it will invoke `r.receive_signal`
+       fun receive_signal(signal: Int) do end
 
-       # Called on any unsafe signal received
-       fun receive_signal_unsafe(signal: Int) `{
-       `}
+       # Called immediatly on receiving an unsafe signal (should be redefed by subclasses)
+       #
+       # Should be used for `sigkill` and `sigstop` since they cannot be caught,
+       # blocked or ignored.
+       #
+       # You should consider this methods as being fragile. It should be implemented in C
+       # and you should not do too much callbacks to Nit.
+       #
+       #     class MyReceiver
+       #         super SignalHandler
+       #
+       #         redef fun receive_signal_unsafe(signal) do print "received unsafely {signal}"
+       #     end
+       #
+       #     var r = new MyReceiver
+       #     r.handle_signal(sigsegv, false) # `r.receive_signal_unsafe` will be invoked on sigsegv
+       fun receive_signal_unsafe(signal: Int) do end
 
        # Set the receiver as the handler of the signal
-       # If safely, receiver will be called when check_signals in invoked
-       #    otherwise the receiver is invoked when the signal is raised, it may
-       #    crash the Nit system but is unavoidable for unstoppable signals
-       fun handle_signal(signal: Int, safely: Bool) import
-               receive_signal `{
-               SignalHandler last_handler;
+       #
+       # If `safely`, receiver will be called when `check_signals` in invoked
+       # otherwise the receiver is invoked when the signal is raised, it may
+       # crash the Nit system but is unavoidable for unstoppable signals.
+       fun handle_signal(signal: Int, safely: Bool) import receive_signal `{
                if (signal < 32 && signal >=0) {
                        struct sigaction act;
                        sigemptyset(&act.sa_mask);
@@ -76,20 +150,26 @@ interface SignalHandler
 
                        sigaction(signal, &act, NULL);
 
-                       last_handler = nit_signals_list[signal].handler;
+               #ifdef SignalHandler_decr_ref
+                       SignalHandler last_handler = (SignalHandler)nit_signals_list[signal].handler;
                        if (last_handler != NULL)
                                SignalHandler_decr_ref(last_handler);
+               #endif
 
-                       nit_signals_list[signal].handler = recv;
-                       SignalHandler_incr_ref(recv);
+                       nit_signals_list[signal].handler = self;
+
+               #ifdef SignalHandler_incr_ref
+                       SignalHandler_incr_ref(self);
+               #endif
 
                        nit_signals_list[signal].safely = safely;
+
+                       nit_SignalHandler_receive_signal = SignalHandler_receive_signal;
                }
        `}
 
        # Set to ignore the signal
        fun ignore_signal(signal: Int) `{
-               SignalHandler last_handler;
                if (signal < 32 && signal >=0) {
                        struct sigaction act;
                        sigemptyset(&act.sa_mask);
@@ -97,15 +177,16 @@ interface SignalHandler
                        act.sa_handler = SIG_IGN;
                        sigaction(signal, &act, NULL);
 
-                       last_handler = nit_signals_list[signal].handler;
+               #ifdef SignalHandler_decr_ref
+                       SignalHandler last_handler = (SignalHandler)nit_signals_list[signal].handler;
                        if (last_handler != NULL)
                                SignalHandler_decr_ref(last_handler);
+               #endif
                }
        `}
 
        # Set default action for the signal
        fun default_signal(signal: Int) `{
-               SignalHandler last_handler;
                if (signal < 32 && signal >=0) {
                        struct sigaction act;
                        sigemptyset(&act.sa_mask);
@@ -113,94 +194,13 @@ interface SignalHandler
                        act.sa_handler = SIG_DFL;
                        sigaction(signal, &act, NULL);
 
-                       last_handler = nit_signals_list[signal].handler;
+               #ifdef SignalHandler_decr_ref
+                       SignalHandler last_handler = (SignalHandler)nit_signals_list[signal].handler;
                        if (last_handler != NULL)
                                SignalHandler_decr_ref(last_handler);
+               #endif
                }
        `}
-
-       # Hang up detected on controlling terminal or death of controlling process
-       fun sighup: Int do return 1
-       
-       # Issued if the user sends an interrupt signal
-       fun sigint: Int do return 2
-       
-       # Issued if the user sends a quit signal
-       fun sigquit: Int do return 3
-       
-       # Issued if the user attempts to execute an illegal, malformed, unknown, or privileged instruction
-       fun sigill: Int do return 4
-       
-       # Issued when an exception occurs: a condition that a debugger has requested to be informed of
-       fun sigtrap: Int do return 5
-       
-       # This signal is sent to a process to tell it to abort, i. e. to terminate
-       fun sigabrt: Int do return 6
-       
-       #This signal is sent to a process when it causes a bus error
-       fun sigbus: Int do return 7
-       
-       # Issued if an illegal mathematical operation is attempted
-       fun sigfpe: Int do return 8
-       
-       # If a process gets this signal it must quit immediately and will not perform any clean-up operations
-       fun sigkill: Int do return 9
-       
-       # Sent to a process to indicate user-defined conditions
-       fun sigusr1: Int do return 10
-       
-       # Sent to a process when it makes an invalid virtual memory reference, or segmentation fault
-       fun sigsegv: Int do return 11
-       
-       # Sent to a process to indicate user-defined conditions
-       fun sigusr2: Int do return 12
-       
-       # Sent to a process when it attempts to write to a pipe without a process connected to the other end
-       fun sigpipe: Int do return 13
-       
-       # Alarm Clock signal
-       fun sigalarm: Int do return 14
-       
-       # Software termination signal
-       fun sigterm: Int do return 15
-       
-       # Sent to a process when a child process terminates or is interrupted
-       fun sigchild: Int do return 17
-       
-       # Tell the operating system to continue (restart) a process previously paused by the SIGSTOP or SIGTSTP signal
-       fun sigcont: Int do return 18
-       
-       # Tell the operating system to stop a process
-       fun sigstop: Int do return 19
-       
-       # Sent to a process by its terminal to request it to stop temporarily
-       fun sigtstp: Int do return 20
-       
-       # Sent to a process when a socket has urgent or out-of-band data available to read
-       fun sigurg: Int do return 23
-       
-       # Sent to a process when it has used the CPU for a duration that exceeds a user-settable value
-       fun sigxcpu: Int do return 24
-       
-       # Sent to a process when it grows a file larger than the maximum allowed size
-       fun sigxfsz: Int do return 25
-       
-       # Virtual timer expired
-       fun sigvtalrm: Int do return 26
-       
-       # Profiling timer expired
-       fun sigprof: Int do return 27
-       
-       # Sent to a process when its controlling terminal changes its window size 
-       fun sigwinch: Int do return 28
-       
-       # Sent to a process when the system experiences a power failure
-       fun sigpwr: Int do return 30
-       
-       # Sent to a process when it passes a bad argument to a system call
-       fun sigsys: Int do return 31
-       
-       
 end
 
 redef interface Object
@@ -216,7 +216,8 @@ redef interface Object
                        if (nit_signals_list[sig].raised) {
                                nit_signals_list[sig].raised = 0;
                                raised_something = 1;
-                               SignalHandler_receive_signal(nit_signals_list[sig].handler, sig);
+                               SignalHandler handler = (SignalHandler)nit_signals_list[sig].handler;
+                               SignalHandler_receive_signal(handler, sig);
                        }
 
                return raised_something;
@@ -226,3 +227,95 @@ redef interface Object
        # can callback any instance of SignalHandler, not just this one
        protected fun set_alarm(sec: Int) `{ alarm(sec); `}
 end
+
+redef class Process
+       # Send a signal to the process
+       fun signal(signal: Int) do native_kill(id, signal)
+
+       # Send the kill signal to the process
+       fun kill do signal(sigkill)
+
+       # Native implementation of `signal`
+       private fun native_kill(pid, signal: Int) `{ kill(pid, signal); `}
+end
+
+# Hang up detected on controlling terminal or death of controlling process
+fun sighup: Int do return 1
+
+# Issued if the user sends an interrupt signal
+fun sigint: Int do return 2
+
+# Issued if the user sends a quit signal
+fun sigquit: Int do return 3
+
+# Issued if the user attempts to execute an illegal, malformed, unknown, or privileged instruction
+fun sigill: Int do return 4
+
+# Issued when an exception occurs: a condition that a debugger has requested to be informed of
+fun sigtrap: Int do return 5
+
+# This signal is sent to a process to tell it to abort, i. e. to terminate
+fun sigabrt: Int do return 6
+
+# This signal is sent to a process when it causes a bus error
+fun sigbus: Int do return 7
+
+# Issued if an illegal mathematical operation is attempted
+fun sigfpe: Int do return 8
+
+# If a process gets this signal it must quit immediately and will not perform any clean-up operations
+fun sigkill: Int do return 9
+
+# Sent to a process to indicate user-defined conditions
+fun sigusr1: Int do return 10
+
+# Sent to a process when it makes an invalid virtual memory reference, or segmentation fault
+fun sigsegv: Int do return 11
+
+# Sent to a process to indicate user-defined conditions
+fun sigusr2: Int do return 12
+
+# Sent to a process when it attempts to write to a pipe without a process connected to the other end
+fun sigpipe: Int do return 13
+
+# Alarm Clock signal
+fun sigalarm: Int do return 14
+
+# Software termination signal
+fun sigterm: Int do return 15
+
+# Sent to a process when a child process terminates or is interrupted
+fun sigchild: Int do return 17
+
+# Tell the operating system to continue (restart) a process previously paused by the SIGSTOP or SIGTSTP signal
+fun sigcont: Int do return 18
+
+# Tell the operating system to stop a process
+fun sigstop: Int do return 19
+
+# Sent to a process by its terminal to request it to stop temporarily
+fun sigtstp: Int do return 20
+
+# Sent to a process when a socket has urgent or out-of-band data available to read
+fun sigurg: Int do return 23
+
+# Sent to a process when it has used the CPU for a duration that exceeds a user-settable value
+fun sigxcpu: Int do return 24
+
+# Sent to a process when it grows a file larger than the maximum allowed size
+fun sigxfsz: Int do return 25
+
+# Virtual timer expired
+fun sigvtalrm: Int do return 26
+
+# Profiling timer expired
+fun sigprof: Int do return 27
+
+# Sent to a process when its controlling terminal changes its window size
+fun sigwinch: Int do return 28
+
+# Sent to a process when the system experiences a power failure
+fun sigpwr: Int do return 30
+
+# Sent to a process when it passes a bad argument to a system call
+fun sigsys: Int do return 31