12.3.14 SP_event()

Synopsis

     #include <sicstus/sicstus.h>
     
     int
     SP_event(int (*func)(void*), void *arg)

Schedules a function for execution in the main thread in contexts where queries cannot be issued.

Arguments

func
The function to schedule for execution.
arg
Its argument.

Return Value

Nonzero on success, and 0 otherwise.

Description

If you wish to call Prolog back from a signal handler that has been installed with SP_signal or a thread other than the thread that called SP_initialize(), that is, the main thread, you cannot use SP_query() etc. directly. The call to Prolog has to be delayed until such time that the Prolog execution can accept an interrupt and the call has to be performed from the main thread (the Prolog execution thread). This function serves this purpose, and installs func to be called from Prolog (in the main thread) when the execution can accept a callback.

A queue of functions, with corresponding arguments, is maintained; that is, if several calls to SP_event() occur before Prolog can accept an interrupt, the functions are queued and executed in turn at the next possible opportunity. A func installed with SP_event() will not be called until SICStus is actually running. One way of ensuring that all pending functions installed with SP_event() are run is to call, from the main thread, some dummy goal, such as,

     SP_query_cut_fail(SP_predicate("true",0,"user"));

While SP_event() is safe to call from any thread, it is not safe to call from arbitrary signal handlers. If you want to call SP_event() when a signal is delivered, you need to install your signal handler with SP_signal().

Note that SP_event() is one of the very few functions in the SICStus API that can safely be called from another thread than the main thread.

Depending on the value returned from func, the interrupted Prolog execution will just continue (SP_SUCCESS) or backtrack (SP_FAILURE or SP_ERROR). An exception raised by func, using SP_raise_exception(), will be processed in the interrupted Prolog execution. If func calls SP_fail() or SP_raise_exception() the return value from func is ignored and handled as if func returned SP_FAILURE or SP_ERROR, respectively. In case of failure or exception, the event queue is flushed.

It is generally not robust to let func raise an exception or fail. The reason is that not all Prolog code is written such that it gracefully handles being interrupted. If you want to interrupt some long-running Prolog code, it is better to let your code test a flag in some part of your code that is executed repeatedly.

Examples

How to install the predicate user:event_pred/1 as the signal handler for SIGUSR1 and SIGUSR2 signals.

The function signal_init() installs the function signal_handler() as the primary signal handler for the signals SIGUSR1 and SIGUSR2. That function invokes the predicate as the actual signal handler, passing the signal number as an argument to the predicate.

     SP_pred_ref event_pred;
     
     static int signal_event(void *handle)
     {
       int signal_no = (int) handle;
       SP_term_ref x=SP_new_term_ref();
       int rc;
     
       SP_put_integer(x, signal_no); // Should not give an error
       rc = SP_query(event_pred, x);
       if (rc == SP_ERROR && SP_exception_term(x))
         SP_raise_exception(x);        // Propagate any raised exception
       return rc;
     }
     
     static void signal_handler(int signal_no)
     {
       SP_event(signal_event, (void *)signal_no);
     }
     
     void signal_init(void)
     {
       event_pred = SP_predicate("prolog_handler",1,"user");
     
       SP_signal(SIGUSR1, signal_handler);
       SP_signal(SIGUSR2, signal_handler);
     }

See Also

Calling Prolog Asynchronously, SP_signal(), SP_fail(), SP_raise_exception().


Send feedback on this subject.