Make thread ThreadId execute Goal at the first
opportunity. The predicate thread_signal/2
itself places Goal into the signalled thread's signal queue
and returns immediately.
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 with EINTR.
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.