10.3 Thread communication
AllApplicationManualNameSummaryHelp

  • Documentation
    • Reference manual
      • Multithreaded applications
        • Thread communication
          • Message queues
          • Waiting for events
            • thread_wait/2
            • thread_update/2
          • Signalling threads
          • Threads and dynamic predicates
    • Packages

10.3.2 Waiting for events

While message queues realizes communicating agents sharing the same program and optionally dynamic data, the predicate thread_wait/2 facilitates agents that communicate based on a shared blackboard. An important difference is were message queues require the sender and receiver to know about the queue used to communicate and every message can wakeup at most one thread, the blackboard model allows any number (including zero) of threads to listen to changes on the blackboard. Any module can act as a blackboard. The blackboard can be updated using the standard Prolog database update predicates (assert/1, retract/1 and friends).

Waiting is implemented using a POSIX condition variable and matching mutex. On a matching database change the condition variable is signalled using a broadcast, waking up all threads waiting in thread_wait/2. Multiple database updates can be grouped and cause a single wakeup using thread_update/2. This predicate also allows signalling the module condition variable without updating the database and controlling whether all or a single thread is activated.

The blackboard architecture is a good match for an intelligent agent system that has to react on a changing world. Input threads gather sensor data from the world and update a shared world view in a set of dynamic predicates in one or more modules. Agent threads listen to this data or a subset thereof and trigger actions. This is notably a good match with tabling, in particular incremental tabling (see section 7.7) and Well Founded Semantics (see section 7.6).201Future versions may provide additional triggers, for example to learn about invalidated tables. Please share your experience.

thread_wait(:Goal, :Options)
Block execution of the calling thread until Goal becomes true. The application must be prepared to handle spurious calls to Goal, i.e., more calls than asked for based on the Options list. A possible exception in Goal is propagated and thus terminates thread_wait/2.

The wait is associated with a module. This module is derived from the Options argument.

The Options list specifies when Goal is re-evaluated and optionally when the call terminates due to a timeout.

deadline(+AbsTime)
timeout(+Time)
Timeout and deadline handling. See thread_get_message/3 for details. This predicate fails when it terminates due to one of these options.
retry_every(+Time)
Retry goal every Time seconds regardless of whether an event happened. The default is 1 second. This ensures that signals (see thread_signal/2) and time limits are respected with an optional delay.202Some operating systems process such signals immediately, while others only check for such events synchronously.
db(+Boolean)
Wakeup on arbitrary changes to any dynamic predicate that is defined in the associated module. This is the default if wait_preds(+Preds) is not provided.
wait_preds(+List)
Only call Goal if at least one of the predicates in List has been modified. Each element of List is a predicate indicator (Name/Arity or Name//Arity that is resolved to a predicate in the module this wait is associated with. If the element is +(PI)203Note that +p/1 is read as /(+(p),1)., Goal is only triggered if a clause was added (assert/1). If the element is -(PI), Goal is only triggered if a clause was retracted (retract/1 or erase/1). Default is to wakeup on both assert and retract.
modified(-List)
The List variable normally also appears in Goal and is unified with a list of predicates from the wait_preds option that have been modified. List must be unbound at entry.
module(+Module)
Specifies the module to act on explicitly.

The execution of Goal is synchronized between all threads calling this predicate on the same module, changes to dynamic predicates in this module and calls to thread_update/2 on the same module.

This predicate raises a permision_error exception when called recursively or called from inside a transaction. See section 4.14.1.2 for details about interaction with transactions.

thread_update(:Goal, :Options)
Update a module (typically using assert/1 and/or retract/1 and friends) and on completion signal threads waiting for this module using thread_wait/2 to reevaluate their Goal. Goal is synchronized between updating and waiting threads. Options:
module(+Module)
Determines the module to operate on. Default is the context module associated with the Options argument.
notify(+Atom)
Determines whether all waiting threads are activated (broadcast, default) or a single thread (signal).

Compatibility The thread_wait/2 predicate is modelled after the Qu-Prolog predicate thread_wait_on_goal/2. It is largely compatible. Our current implementation does not support predicate time stamps.204See predicate_property/2, property generation. We made this predicate act on a specific module rather than the entire database. The timeout specification follows that of the other thread waiting predicates and may be combined with the retry_every option. The default retry-time is also 1 second rather than infinite.