The features described in this section are only enabled on Unix
systems providing POSIX threads and if the system is configured using
the --enable-mt option. SWI-Prolog multi-theading
support is experimental and in some areas not safe.
SWI-Prolog multithreading is based on standard C-language
multithreading support. It is not like ParLog or other paralel
implementations of the Prolog language. Prolog threads have their own
stacks and only share the Prolog heap: predicates, records,
flags and other global non-backtrackable data. SWI-Prolog thread support
is designed with the following goals in mind.
- Multi-threaded server applications
Todays computing services often focus on (internet) server applications.
Such applications often have need for communication between services
and/or fast non-blocking service to multiple concurrent clients. The
shared heap provides fast communication and thread creation is
relatively cheap (A Pentium-II/450 can create and join approx. 10,000
threads per second on Linux 2.2).
- Interactive applications
Interactive applications often need to perform extensive computation. If
such computations are executed in a new thread, the main thread can
process events and allow the user to cancel the ongoing computation.
User interfaces can also use multiple threads, each thread dealing with
input from a distinct group of windows.
- Natural integration with foreign code
Each Prolog thread runs in a C-thread, automatically making them
cooperate with MT-safe foreign-code. In addition, any foreign
thread can create its own Prolog engine for dealing with calling Prolog
from C-code.
- thread_create(:Goal,
-Id, +Options)
-
Create a new Prolog thread (and underlying C-thread) and start it by
executing Goal. If the thread is created succesfully, the
thread-identifier of the created thread is unified to Id.
Options is a list of options. Currently defined options are:
- local(K-Bytes)
-
Set the limit to which the local stack of this thread may grow. If
omited, the limit of the calling thread is used. See also the
-L commandline option.
- global(K-Bytes)
-
Set the limit to which the global stack of this thread may grow. If
omited, the limit of the calling thread is used. See also the
-G commandline option.
- trail(K-Bytes)
-
Set the limit to which the trail stack of this thread may grow. If
omited, the limit of the calling thread is used. See also the
-T commandline option.
- argument(K-Bytes)
-
Set the limit to which the argument stack of this thread may grow. If
omited, the limit of the calling thread is used. See also the
-A commandline option.
- alias(AliasName)
-
Associate an `alias-name' with the thread. This named may be used to
refer to the thread and remains valid until the thread is joined (see thread_join/2).
- detached(Bool)
-
If
false
(default), the thread can be waited for using
thread_join/2. thread_join/2
must be called on this thread to reclaim the all resources associated to
the thread. If true
, the system will reclaim all associated
resources automatically after the thread finishes. Please not that
thread identifiers are freed for reuse after a detached thread finishes
or a normal thread has been joined.
The Goal argument is copied to the new Prolog
engine. This implies further instantiation of this term in either thread
does not have consequences for the other thread: Prolog threads do not
share data from their stacks.
- thread_self(-Id)
-
Get the Prolog thread identifier of the running thread. If the thread
has an alias, the alias-name is returned.
- current_thread(?Id,
?Status)
-
Enumerates identifiers and status of all currently known threads.
Calling current_thread/2
does not influence any thread. See also
thread_join/2.
For threads that have an alias-name, this name is returned in Id
instead of the numerical thread identifier.
Status is one of:
- running
-
The thread is running. This is the initial status of a thread. Please
note that threats waiting for something are considered running too.
- false
-
The Goal of the thread has been completed and failed.
- true
-
The Goal of the thread has been completed and succeeded.
- exited(Term)
-
The Goal of the thread has been terminated using thread_exit/1
with Term as argument.
- exception(Term)
-
The Goal of the thread has been terminated due to an uncaught
exception (see throw/1
and catch/3).
- thread_join(+Id,
-Status)
-
Wait for the termination of thread with given Id. Then unify
the result-status (see thread_exit/1)
of the thread with Status. After this call, Id
becomes invalid and all resources associated with the thread are
reclaimed. See also current_thread/2.
A thread that has been completed without thread_join/2
being called on it is partly reclaimed: the Prolog stacks are released
and the C-thread is destroyed. A small data-structure represening the
exit-status of the thread is retained until thread_join/2
is called on the thread.
- thread_exit(+Term)
-
Terminates the thread immediately, leaving
exited(Term)
as
result-state. The Prolog stacks and C-thread are reclaimed.
- thread_at_exit(:Goal)
-
Run Goal after the execution of this thread has terminated.
This is to be compared to at_halt/1,
but only for the current thread. These hooks are ran regardless of why
the execution of the thread has been completed. As these hooks are run,
the return-code is already available through current_thread/2.
Prolog threads can exchange data using dynamic predicates, database
records, and other globally shared data. In addition, they can send
messages to each other. If a threads needs to wait for another thread
until that thread has produced some data, using only the database forces
the waiting thread to poll the database continuously. Waiting for a
message suspends the thread execution until the message has arrived in
its message queue.
- thread_send_message(+ThreadId,
+Term)
-
Place Term in the message queue of the indicated thread
(which can even be the message queue of itself (see thread_self/1).
Any term can be placed in a message queue, but note that the term is
copied to to receiving thread and variable-bindings are thus lost. This
call returns immediately.
- thread_get_message(?Term)
-
Examines the thread message-queue and if necessary blocks execution
until a term that unifies to Term arrives in the queue. After
a term from the queue has been unified unified to Term, this
term is deleted from the queue and this predicate returns.
Please note that not-unifying messages remain in the queue. After the
following has been executed, thread 1 has the term b(gnu)
in its queue and continues execution using A is gnat
.
<thread 1>
thread_get_message(a(A)),
<thread 2>
thread_send_message(b(gnu)),
thread_send_message(a(gnat)),
|
See also thread_peek_message/1.
- thread_peek_message(?Term)
-
Examines the thread message-queue and compares the queued terms with Term
until one unifies or the end of the queue has been reached. In the first
case the call succeeds (possibly instantiating
Term. If no term from the queue unifies this call fails.
- thread_signal(+ThreadId,
:Goal)
-
Make thread ThreadId execute Goal at the first
opportunity. In the current implementation, this implies at the first
pass through the Call-port. The predicate thread_signal/2
itself places Goal into the signalled-thread's signal queue
and returns immediately.
Signals (interrupts) do not cooperate well with the world of
multi-threading, mainly because the status of mutexes cannot be
guaranteed easily. At the call-port, the Prolog virtual machine holds no
locks and therefore the asynchronous execution is safe.
Goal can be any valid Prolog goal, including throw/1
to make the receiving thread generate an exception and trace/0
to start tracing the receiving thread.
All internal Prolog operations are thread-safe. This implies two
Prolog threads can operate on the same dynamic predicate without
corrupting the consistency of the predicate. This section deals with
user-level
mutexes (called monitors in ADA or
critical-sections by Microsoft). A mutex is a
MUTual EXclusive device, which implies at most one thread
can hold a mutex.
Mutexes are used to realise related updates to the Prolog database.
With `related', we refer to the situation where a `transaction' implies
two or more changes to the Prolog database. For example, we have a
predicate address/2, representing
the address of a person and we want to change the address by retracting
the old and asserting the new address. Between these two operations the
database is invalid: this person has either no address or two addresses
(depending on the assert/retract order).
Here is how to realise a correct update:
:- initialization
mutex_create(addressbook).
change_address(Id, Address) :-
mutex_lock(addressbook),
retractall(address(Id, _)),
asserta(address(Id, Address)),
mutex_unlock(addressbook).
|
- mutex_create(?MutexId)
-
Create a mutex. if MutexId is an atom, a named mutex
is created. If it is a variable, an anonymous mutex reference is
returned. There is no limit to the number of mutexes that can be
created.
- mutex_destroy(+MutexId)
-
Destroy a mutex. After this call, MutexId becomes invalid and
further references yield an
existence_error
exception.
- mutex_lock(+MutexId)
-
Lock the mutex. Prolog mutexes are recursive mutexes: they can
be locked multiple times by the same thread. Only after unlocking it as
many times as it is locked, the mutex becomes available for locking by
other threads. If another thread has locked the mutex the calling thread
is suspended until to mutex is unlocked.
If MutexId is an atom, and there is no current mutex with
that name, the mutex is created automatically using mutex_create/1.
This implies named mutexes need not be declared explicitly.
Please note that locking and unlocking mutexes should be paired
carefully. Especially make sure to unlock mutexes even if the protected
code fails or raises an exception. For most common cases use
with_mutex/2,
wich provides a safer way for handling prolog-level mutexes.
- mutex_trylock(+MutexId)
-
As mutex_lock/1,
but if the mutex is held by another thread, this predicates fails
immediately.
- mutex_unlock(+MutexId)
-
Unlock the mutex. This can only be called if the mutex is held by the
calling thread. If this is not the case, a
permission_error
exception is raised.
- mutex_unlock_all
-
Unlock all mutexes held by the current thread. This call is especially
useful to handle thread-termination using abort/0
or exceptions. See also thread_signal/2.
- current_mutex(?MutexId,
?ThreadId, ?Count)
-
Enumerates all existing mutexes. If the mutex is held by some thread,
ThreadId is unified with the identifier of te holding thread
and
Count with the recursive count of the mutex. Otherwise,
ThreadId is
and Count is 0.
- with_mutex(+MutexId,
:Goal)
-
Execute Goal while holding MutexId. If Goal
leaves choicepointes, these are destroyed (as in once/1).
The mutex is unlocked regardless of whether Goal succeeds,
fails or raises an exception. An exception thrown by Goal is
re-thrown after the mutex has been successfully unlocked. See also mutex_create/2.
Although described in the thread-section, this predicate is also
available in the single-threaded version, where it behaves simply as
once/1.
This library defines a couple of useful predicates for demonstrating
and debugging multi-threaded applications. This library is certainly not
complete.
- threads
-
Lists all current threads and their status. In addition, all `zombie'
threads (finished threads that are not detached, nor waited for) are
joined to reclaim their resources.
- interactor
-
Create a new console and run the Prolog toplevel in this new console.
See also attach_console/0.
- attach_console
-
If the current thread has no console attached yet, attach one and
redirect the user streams (input, output, and error) to the new console
window. The console is an xterm application. For this to work,
you should be running X-windows and your xterm should know the
-Sccn.
This predicate has a couple of useful applications. One is to
separate (debugging) I/O of different threads. Another is to start
debugging a thread that is running in the background. If thread 10 is
running, the following sequence starts the tracer on this thread:
?- thread_signal(10, (attach_console, trace)).
|
It is assumed that the basic Prolog execution is thread-safe. Various
problems are to be expected though, both dead-locks as well as
not-thread-safe code in builtin-predicates.