The predicates in this section provide signalling between threads. A thread signal inserts any goal as an interrupt into the control flow of any target thread. The target thread processes the goal at the first safe opportunity. The mechanism was introduced with two goals in mind: (1) running a goal inside a thread for debugging purposes such as enabling the status or get access thread-specific data and (2) force a thread to abort its current goal by inserting an exeption into its control flow.
Over time, more complicated use cases have been identified that may result in multiple signals that occur (nearly) simultaneous. As of version 8.5.1 the interface has been extended and the interaction with other built-in predicates has been specified in much more detail.
ThreadId executes Goal as an interrupt at the first opportunity. Defined opportunities are:
sig_atomic
. Currently this only applies
to sig_atomic/1.FALSE
after PL_handle_signals()
returned -1, indicating an exception was raised.SIGUSR2
to the signalled thread while the handler is an
empty function. This causes most blocking system calls to return with EINTR
.
See also the commandline option
--sig-alert. On Windows, PL_handle_signals()
is called when the user processes Windows messages.If one or more signals are queued, the queue is processed. Processing the queue skips signals blocked due to sig_block/1 and stops after the queue does not contain any more non-blocked signals or processing a signal results in an exception. After an exception, other signals remain in the queue and will be processed after unwinding to the matching catch/3. Typically these queued signals will be processed during the Recover goal of the catch/3. Note that sig_atomic/1 may be used to protect the recovery goal.
The thread_signal/2
mechanism is primarily used by the system to insert debugging goals into
the target thread (tspy/1, tbacktrace/1,
etc.) or to interrupt a thread using e.g., thread_signal(Thread,
abort)
. Predicates from library library(thread)
use
signals to stop workers for e.g. concurrent_maplist/2
if some call fails. Applications may use it, typically for similar
purposes such as asynchronously stopping tasks or inspecting the status
of a task. Below we describe the behaviour of thread signalling in more
detail. The following notes apply for
Goal executing in ThreadId
The call port of sig_atomic/1 does not handle signals. This may notably be used to prevent interruption of the catch/3 Recover goal. For example, we may ensure the recovery goal of a timeout is called using the code below. Without this precaution another signal may run before writeln/1 and raise an exception to prevent its execution. Note that catch/3 should generally not be used for cleanup of resources in case of an exception and thus it is typically fine if its Recover goal is interrupted. Use setup_call_cleanup/3 or one of the other predicates from the call_cleanup/2 family for cleanup.
..., catch(call_with_time_limit(Time, Goal), time_limit_exceeded, sig_atomic(writeln('Time limit exceeded'))).