10.3.3 Signalling threads
AllApplicationManualNameSummaryHelp

  • Documentation
    • Reference manual
      • Multithreaded applications
        • Thread communication
          • Signalling threads
            • thread_signal/2
            • sig_pending/1
            • sig_remove/2
            • sig_block/1
            • sig_unblock/1
            • sig_atomic/1
    • Packages
Availability:built-in
[det]thread_signal(+ThreadId, :Goal)
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.