next up previous index
Next: Debugging Up: Events and Interrupts Previous: Errors   Index

Subsections


Interrupts

Operating systems such as Unix provide a notion of asynchronous interrupts or signals. In a standalone ECLiPSe system, the signals can be handled by defining interrupt handlers for them. In fact, a set of default handlers is already predefined in this case, which can also be used for an embedded ECLiPSe.

In an embedded ECLiPSe, signals are usually handled by the host application. It is recommended to use the event mechanism (the ec_post_event() library function) when signals are meant to be handled by ECLiPSe code (via the event/1 default handler).

Interrupt Identifiers

Interrupts are identified either by their signal number (Unix) or by a name which is derived from the name the signal has in the operating system. Most built-ins understand both identifiers. It is usually more portable to use the symbolic name. The built-in current_interrupt/2 is provided to check and/or generate the valid interrupt numbers and their mnemonic names.

Asynchronous handling

When an interrupt happens, the ECLiPSe system calls an interrupt handling routine in a manner very similar to the case of event handling. The only argument to the handler is the interrupt number. Just as event handlers may be user defined, so it is possible to define interrupt handlers. The goal

set_interrupt_handler(N, PredSpec)
assigns the procedure specified by PredSpec as the interrupt handler for the interrupt identified by N (a number or a name). Some interrupts cannot be caught by the user (e.g. the kill signal), trying to establish a handler for them yields an error message. Note that in a standalone ECLiPSe, PredSpec can be a user defined predicate, but on embedded systems, PredSpec must be one of the predefined handlers.

To test interrupt handlers, the built-in kill/2 may be used to send a signal to the own process.

The predicate get_interrupt_handler/3 may be used to find the current interrupt handler for an interrupt N, in the same manner as get_event_handler:

get_interrupt_handler(N, PredSpec, HomeModule)

An interrupt handler has one optional argument, which is the interrupt number. There is no argument corresponding to the error culprit, since the interrupt has no relation to the currently executed predicate. A handler may be defined which takes no argument (such as when the handler is defined for only one interrupt type). If the handler has one argument, the identifier of the interrupt is passed to the handler when it is called.

When an interrupt occurs, the system halts what it is currently doing and calls the interrupt handler. Just as in the case with error handling, the interrupt handler can be any Prolog procedure. However, unlike the situation in the case of error handling, when the handler exits, be it with success or failure, the execution is resumed at the point where it was interrupted, the interrupt handling is in this case completely independent13.4. This ``resume and forget'' policy means that to the Prolog program, an interrupt is ``invisible'' -- providing the handler has no side effects, the program continues as if the interrupt had never happened. As a consequence it is not significant whether the handler succeeds or fails. However, again just as in the case of error handlers, a call to the predicate exit_block/1 may be made in order to escape from within the handler to the corresponding call of block/3. Obviously, in this case the interrupted execution can no longer be resumed.

There are a few special predefined interrupt handlers:

default/0

performs the standard UNIX handling of the specified interrupt (signal). Setting this handler is equivalent to calling signal(N, SIG_DFL) on the C level. Thus e.g. specifying
    ?- set_interrupt_handler(int, default/0)
will exit the ECLiPSe system when ^C is pressed.

true/0

This is equivalent to calling signal(N, SIG_IGN) on the C level, ie. the interrupt is ignored.

event/1

The signal is handled by posting a (synchronous) event. The event name is the symbolic name of the interrupt.
throw/1

Invoke exit_block/1 with the interupt's symbolic name.
abort/0

Invoke exit_block(abort).
halt/0

Terminate the ECLiPSe process.
internal/0
Used by ECLiPSe to implement internal functionality like the profiler. This is not intended to be used by the user.
Apart from these special cases, all other arguments will result in the specified predicate to be called when the appropriate interrupt occurs. This general asynchronous interrupt handling is not supported on some hardware/platforms, e.g. Windows, and also not on an embedded ECLiPSe.

Note that tkeclipse is an embedded ECLiPSe running under Tcl/Tk, so the general asynchronous interrupt handling is not supported on it.


Example

Here is an example for the use of after events for implementing a time-out predicate. The fact that the event is handled sychronously is important: it allows the the running excecution to cleanly abort at a well-defined point in execution.
timeout(Goal, Seconds, Timer, TimeOutGoal) :-
% allows the user to set up different Timers 
set_event_handler(Timer, exit_block/1),
        start_timer(Timer, Seconds),
        block(
            (call(Goal) -> 
 % need to make sure timer is stopped after executing TimeOutGoal
stop_timer(Timer) 
            ;  stop_timer(Timer), fail
            ),
            Tag,
            handle_timeout(Tag, Timer, TimeOutGoal)
        ).

start_timer(_, 0) :- !. % 0 means no timeout
start_timer(Timer, Timeout) :- 
        event_after(Timer, Timeout).

stop_timer(Timer) :- 
        ( cancel_after_event(Timer) -> true ; true ).


handle_timeout(Tag, Timer, TimeOutGoal) :-
        (Tag == Timer ->
             call(TimeOutGoal) % timed out, call the TimeOutGoal
;    stop_timer(Timer), % TimeOutGoal aborted for other reasons
exit_block(Tag)
        ).


next up previous index
Next: Debugging Up: Events and Interrupts Previous: Errors   Index
Warwick Harvey
2004-08-07