SP_event()
#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.
Nonzero on success, and 0 otherwise.
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.
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); }
Calling Prolog Asynchronously,
SP_signal()
,
SP_fail()
,
SP_raise_exception()
.