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.

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.

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 can not be caught by the user (e.g. the kill signal), trying to establish a handler for them yields an error message.

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_error_handler:

get_interrupt_handler(N, PredSpec, HomeModule)
The predicates reset_interrupt_handler/1 and reset_interrupt_handlers/0 are used to reset a particular interrupt handler or all interrupt handlers to their default values.

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 independent14.2. 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 settings for 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.
Apart from these special cases, all other arguments will result in the specified predicate to be called when the appropriate interrupt occurs.


Example

Here is an example for the use of an asynchronous timer signal and a synchronous event handler for implementing a time-out predicate. Mapping the interrupt to an event is necessary in order to cleanly abort the running excecution at a well-defined point in execution.
timeout(Goal, Seconds, TimeOutGoal) :-
        block(
            timeout_once(Goal, Seconds),
            timeout,
            call(TimeOutGoal)
        ).

timeout_once(Goal, Seconds) :-
        set_timer(real,Seconds),
        call(Goal),
        !,
        set_timer(real,0).
timeout_once(_, _) :-
        set_timer(real,0),
        fail.

timeout_handler :-
        set_timer(real,0),
        exit_block(timeout).

:- set_interrupt_handler(alrm, event/1).
:- set_event_handler(alrm, timeout_handler/0).


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

1999-08-06