ThreadId executes Goal as an interrupt at the first opportunity. Defined opportunities are:
- At the call port of any predicate except for predicates
with the property
sig_atomic
. Currently this only applies to sig_atomic/1. - Before retrying a foreign predicate.
- Before backtracking to the next clause of a Prolog predicate.
- When a foreign predicate calls PL_handle_signals().
Foreign predicates that take long to complete should call
PL_handle_signals()
regularly and return with
FALSE
after PL_handle_signals() returned -1, indicating an exception was raised. - Foreign predicates calling blocking system calls should
attempt to make these system calls interruptible. To enable this on
POSIX systems, SWI-Prolog sends a
SIGUSR2
to the signalled thread while the handler is an empty function. This causes most blocking system calls to return withEINTR
. See also the commandline option --sig-alert. On Windows, PL_handle_signals() is called when the user processes Windows messages. - For some blocking (thread) APIs we use a timed version with a 0.25 sec timeout to achieve a polling loop.
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 execution is protected by sig_atomic/1 and thus signal execution is not nested.
- If Goal succeeds, possible choice points are discarded. Changes to the Prolog stacks such as changes to backtrackable global variables remain.
- If Goal fails, no action is taken, i.e., failure is not considered a special condition.
- If Goal raises an exception the exeception is propagated into the environment. This allows for forcefully stopping the target thread. The system uses this to implement abort/0 and call_with_time_limit/2.
- Code into which signals may be injected must make sure to use setup_call_cleanup/3 and friends to ensure proper cleanup in the case of an exception. This is good practice anyway to guard against unpredicatable exceptions such as resource exhaustion.
- Goal may use stack inspection such as prolog_frame_attribute/3 to determine what the thread is doing.