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).183Future 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.184Some 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
185Note that+
(PI)+p/1
is read as /(+(p),1)., Goal is only triggered if a clause was added (assert/1). If the element is
, Goal is only triggered if a clause was retracted (retract/1 or erase/1). Default is to wakeup on both assert and retract.-
(PI) - 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.
- 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.186See 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.