documentation for thread support (stolen from Jan's SWI docs).
git-svn-id: https://yap.svn.sf.net/svnroot/yap/trunk@1021 b08c6af1-5177-4d33-ba66-4b1c6b8b522a
This commit is contained in:
parent
cd3142c211
commit
dbe36978b6
291
docs/yap.tex
291
docs/yap.tex
@ -8,7 +8,7 @@
|
|||||||
@c @setchapternewpage odd
|
@c @setchapternewpage odd
|
||||||
@c %**end of header
|
@c %**end of header
|
||||||
|
|
||||||
@set VERSION: 4.5.2
|
@set VERSION 4.5.2
|
||||||
@set EDITION 4.2.3
|
@set EDITION 4.2.3
|
||||||
@set UPDATED January 2002
|
@set UPDATED January 2002
|
||||||
|
|
||||||
@ -7737,7 +7737,7 @@ mailing-list.
|
|||||||
@findex splay_access/5
|
@findex splay_access/5
|
||||||
@snindex splay_access/5
|
@snindex splay_access/5
|
||||||
@cnindex splay_access/5
|
@cnindex splay_access/5
|
||||||
If item @var{Key} is in tree @var{Tree}, return its @value{Val} and
|
If item @var{Key} is in tree @var{Tree}, return its @var{Val} and
|
||||||
unify @var{Return} with @code{true}. Otherwise unify @var{Return} with
|
unify @var{Return} with @code{true}. Otherwise unify @var{Return} with
|
||||||
@code{null}. The variable @var{NewTree} unifies with the new tree.
|
@code{null}. The variable @var{NewTree} unifies with the new tree.
|
||||||
|
|
||||||
@ -12124,13 +12124,13 @@ Logtalk documentation is included in the Logtalk directory. For the
|
|||||||
latest news, please see the URL @url{http://www.logtalk.org/}.
|
latest news, please see the URL @url{http://www.logtalk.org/}.
|
||||||
|
|
||||||
@node Threads, Parallelism, Logtalk, Extensions
|
@node Threads, Parallelism, Logtalk, Extensions
|
||||||
@chapter Parallelism
|
@chapter Threads
|
||||||
|
|
||||||
YAP implements a SWI-Prolog compatible multithreading
|
YAP implements a SWI-Prolog compatible multithreading
|
||||||
library. Like in SWI-Prolog, Prolog threads have their own stacks and
|
library. Like in SWI-Prolog, Prolog threads have their own stacks and
|
||||||
only share the Prolog \emph{heap}: predicates, records, flags and other
|
only share the Prolog @emph{heap}: predicates, records, flags and other
|
||||||
global non-backtrackable data. The package is based on the POSIX thread
|
global non-backtrackable data. The package is based on the POSIX thread
|
||||||
standard \cite{Butenhof:1997:PPT} used on most popular systems except
|
standard (Butenhof:1997:PPT) used on most popular systems except
|
||||||
for MS-Windows.
|
for MS-Windows.
|
||||||
|
|
||||||
@comment On Windows it uses the
|
@comment On Windows it uses the
|
||||||
@ -12140,15 +12140,19 @@ for MS-Windows.
|
|||||||
|
|
||||||
@menu
|
@menu
|
||||||
Subnodes of Threads
|
Subnodes of Threads
|
||||||
* Creating and destroying Prolog threads::
|
* Creating and Destroying Prolog Threads::
|
||||||
* Monitoring threads::
|
* Monitoring Threads::
|
||||||
* Thread communication::
|
* Thread Communication::
|
||||||
* Thread synchronisation::
|
* Thread Synchronisation::
|
||||||
|
|
||||||
|
Subnodes of Thread Communication
|
||||||
|
* Message Queues::
|
||||||
|
* Signalling Threads::
|
||||||
|
* Threads and Dynamic Predicates::
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@node Creating and destroying Prolog threads, Monitoring Threads, ,Threads
|
@node Creating and Destroying Prolog Threads, Monitoring Threads, ,Threads
|
||||||
@section Creating and destroying Prolog threads
|
@section Creating and Destroying Prolog Threads
|
||||||
|
|
||||||
@table @code
|
@table @code
|
||||||
|
|
||||||
@ -12166,12 +12170,12 @@ thread-identifier of the created thread is unified to @var{Id}.
|
|||||||
@item stack
|
@item stack
|
||||||
Set the limit in K-Bytes to which the Prolog stacks of
|
Set the limit in K-Bytes to which the Prolog stacks of
|
||||||
this thread may grow. If omited, the limit of the calling thread is
|
this thread may grow. If omited, the limit of the calling thread is
|
||||||
used. See also the \cmdlineoption{-s} commandline option.
|
used. See also the commandline @code{-S} option.
|
||||||
|
|
||||||
@item trail
|
@item trail
|
||||||
Set the limit in K-Bytes to which the trail stack of this thread may
|
Set the limit in K-Bytes to which the trail stack of this thread may
|
||||||
grow. If omited, the limit of the calling thread is used. See also the
|
grow. If omited, the limit of the calling thread is used. See also the
|
||||||
\cmdlineoption{-t} commandline option.
|
commandline option @code{-T}.
|
||||||
|
|
||||||
@item alias
|
@item alias
|
||||||
Associate an alias-name with the thread. This named may be used to
|
Associate an alias-name with the thread. This named may be used to
|
||||||
@ -12188,7 +12192,7 @@ after a detached thread finishes or a normal thread has been joined.
|
|||||||
See also @code{thread_join/2} and @code{thread_detach/1}.
|
See also @code{thread_join/2} and @code{thread_detach/1}.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
The @var{Goal} argument is \emph{copied} to the new Prolog engine.
|
The @var{Goal} argument is @emph{copied} to the new Prolog engine.
|
||||||
This implies further instantiation of this term in either thread does
|
This implies further instantiation of this term in either thread does
|
||||||
not have consequences for the other thread: Prolog threads do not share
|
not have consequences for the other thread: Prolog threads do not share
|
||||||
data from their stacks.
|
data from their stacks.
|
||||||
@ -12228,8 +12232,8 @@ The goal has failed.
|
|||||||
exception. See @code{print_message/2} to turn system exceptions into
|
exception. See @code{print_message/2} to turn system exceptions into
|
||||||
readable messages.
|
readable messages.
|
||||||
|
|
||||||
@item exited(@var{Term)
|
@item exited(@var{Term})
|
||||||
The thread is terminated on thread_exit/1 using the argument @var{Term}.
|
The thread is terminated on @code{thread_exit/1} using the argument @var{Term}.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
|
||||||
@ -12237,7 +12241,7 @@ The thread is terminated on thread_exit/1 using the argument @var{Term}.
|
|||||||
@findex thread_detach/1
|
@findex thread_detach/1
|
||||||
@snindex thread_detach/1
|
@snindex thread_detach/1
|
||||||
@cnindex thread_detach/1
|
@cnindex thread_detach/1
|
||||||
Switch thread into detached-state (see \code{detached} option at
|
Switch thread into detached-state (see @code{detached} option at
|
||||||
@code{thread_create/3} at runtime. @var{Id} is the identifier of the thread
|
@code{thread_create/3} at runtime. @var{Id} is the identifier of the thread
|
||||||
placed in detached state.
|
placed in detached state.
|
||||||
|
|
||||||
@ -12255,7 +12259,7 @@ inspected.
|
|||||||
@cnindex thread_exit/1
|
@cnindex thread_exit/1
|
||||||
Terminates the thread immediately, leaving @code{exited(@var{Term})} as
|
Terminates the thread immediately, leaving @code{exited(@var{Term})} as
|
||||||
result-state for @code{thread_join/2}. If the thread has the attribute
|
result-state for @code{thread_join/2}. If the thread has the attribute
|
||||||
\term{detached}{true} it terminates, but its exit status cannot be
|
@code{detached} @code{true} it terminates, but its exit status cannot be
|
||||||
retrieved using @code{thread_join/2} making the value of @var{Term}
|
retrieved using @code{thread_join/2} making the value of @var{Term}
|
||||||
irrelevant. The Prolog stacks and C-thread are reclaimed.
|
irrelevant. The Prolog stacks and C-thread are reclaimed.
|
||||||
|
|
||||||
@ -12284,8 +12288,8 @@ and succeeds silently.
|
|||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
|
||||||
@node Monitoring threads, Thread Communication,Creating and destroying Prolog threads,Threads
|
@node Monitoring Threads, Thread Communication,Creating and Destroying Prolog Threads,Threads
|
||||||
@section Monitoring threads
|
@section Monitoring Threads
|
||||||
|
|
||||||
Normal multi-threaded applications should not need these the predicates
|
Normal multi-threaded applications should not need these the predicates
|
||||||
from this section because almost any usage of these predicates is
|
from this section because almost any usage of these predicates is
|
||||||
@ -12302,7 +12306,6 @@ These predicates are provided for diagnosis and monitoring tasks.
|
|||||||
@findex current_thread/2
|
@findex current_thread/2
|
||||||
@snindex current_thread/2
|
@snindex current_thread/2
|
||||||
@cnindex current_thread/2
|
@cnindex current_thread/2
|
||||||
\predicate{current_thread}{2}{?Id, ?Status}
|
|
||||||
Enumerates identifiers and status of all currently known threads.
|
Enumerates identifiers and status of all currently known threads.
|
||||||
Calling current_thread/2 does not influence any thread. See also
|
Calling current_thread/2 does not influence any thread. See also
|
||||||
@code{thread_join/2}. For threads that have an alias-name, this name is
|
@code{thread_join/2}. For threads that have an alias-name, this name is
|
||||||
@ -12343,7 +12346,6 @@ stacks and CPU time yield different values for each thread.
|
|||||||
@findex mutex_statistics/0
|
@findex mutex_statistics/0
|
||||||
@snindex mutex_statistics/0
|
@snindex mutex_statistics/0
|
||||||
@cnindex mutex_statistics/0
|
@cnindex mutex_statistics/0
|
||||||
\predicate{mutex_statistics}{0}{}
|
|
||||||
Print usage statistics on internal mutexes and mutexes associated
|
Print usage statistics on internal mutexes and mutexes associated
|
||||||
with dynamic predicates. For each mutex two numbers are printed:
|
with dynamic predicates. For each mutex two numbers are printed:
|
||||||
the number of times the mutex was acquired and the number of
|
the number of times the mutex was acquired and the number of
|
||||||
@ -12355,103 +12357,121 @@ close to zero on single-CPU hardware.
|
|||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
|
||||||
@node Thread Communication, ,Monitoring threads, Threads
|
@node Thread Communication, Thread Synchronisation, Monitoring Threads, Threads
|
||||||
@section Thread communication
|
@section Thread communication
|
||||||
|
|
||||||
\subsection{Message queues} \label{sec:msgqueue}
|
@menu
|
||||||
|
Subnodes of Thread Communication
|
||||||
|
* Message Queues::
|
||||||
|
* Signalling Threads::
|
||||||
|
* Threads and Dynamic Predicates::
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node Message Queues, Signalling Threads, ,Thread Communication
|
||||||
|
@subsection Message Queues
|
||||||
|
|
||||||
Prolog threads can exchange data using dynamic predicates, database
|
Prolog threads can exchange data using dynamic predicates, database
|
||||||
records, and other globally shared data. These provide no suitable means
|
records, and other globally shared data. These provide no suitable means
|
||||||
to wait for data or a condition as they can only be checked in an
|
to wait for data or a condition as they can only be checked in an
|
||||||
expensive polling loop. \jargon{Message queues} provide a means for
|
expensive polling loop. @emph{Message queues} provide a means for
|
||||||
threads to wait for data or conditions without using the CPU.
|
threads to wait for data or conditions without using the CPU.
|
||||||
|
|
||||||
Each thread has a message-queue attached to it that is identified
|
Each thread has a message-queue attached to it that is identified
|
||||||
by the thread. Additional queues are created using
|
by the thread. Additional queues are created using
|
||||||
message_queue_create/2.
|
@code{message_queue_create/2}.
|
||||||
|
|
||||||
@table @code
|
@table @code
|
||||||
\predicate{thread_send_message}{2}{+QueueOrThreadId, +Term}
|
@item thread_send_message(+@var{QueueOrThreadId}, +@var{Term})
|
||||||
|
@findex thread_send_message/2
|
||||||
|
@snindex thread_send_message/2
|
||||||
|
@cnindex thread_send_message/2
|
||||||
Place @var{Term} in the given queue or default queue of the indicated
|
Place @var{Term} in the given queue or default queue of the indicated
|
||||||
thread (which can even be the message queue of itself (see
|
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
|
@code{thread_self/1}). Any term can be placed in a message queue, but note that
|
||||||
the term is copied to the receiving thread and variable-bindings are
|
the term is copied to the receiving thread and variable-bindings are
|
||||||
thus lost. This call returns immediately.
|
thus lost. This call returns immediately.
|
||||||
|
|
||||||
If more than one thread is waiting for messages on the given queue and
|
If more than one thread is waiting for messages on the given queue and
|
||||||
at least one of these is waiting with a partially instantiated
|
at least one of these is waiting with a partially instantiated
|
||||||
@var{Term}, the waiting threads are \emph{all} sent a wakeup signal,
|
@var{Term}, the waiting threads are @emph{all} sent a wakeup signal,
|
||||||
starting a rush for the available messages in the queue. This behaviour
|
starting a rush for the available messages in the queue. This behaviour
|
||||||
can seriously harm performance with many threads waiting on the same
|
can seriously harm performance with many threads waiting on the same
|
||||||
queue as all-but-the-winner perform a useless scan of the queue. If
|
queue as all-but-the-winner perform a useless scan of the queue. If
|
||||||
there is only one waiting thread or all waiting threads wait with an
|
there is only one waiting thread or all waiting threads wait with an
|
||||||
unbound variable an arbitrary thread is restarted to scan the queue.%
|
unbound variable an arbitrary thread is restarted to scan the queue.%
|
||||||
\footnote{See the documentation for the POSIX thread functions
|
@comment \footnote{See the documentation for the POSIX thread functions
|
||||||
pthread_cond_signal() v.s.\ pthread_cond_broadcastt()
|
@comment pthread_cond_signal() v.s.\ pthread_cond_broadcastt()
|
||||||
for background information.}
|
@comment for background information.}
|
||||||
|
|
||||||
\predicate{thread_get_message}{1}{?Term}
|
@item thread_get_message(?@var{Term})
|
||||||
|
@findex thread_get_message/1
|
||||||
|
@snindex thread_get_message/1
|
||||||
|
@cnindex thread_get_message/1
|
||||||
Examines the thread message-queue and if necessary blocks execution
|
Examines the thread message-queue and if necessary blocks execution
|
||||||
until a term that unifies to @var{Term} arrives in the queue. After
|
until a term that unifies to @var{Term} arrives in the queue. After
|
||||||
a term from the queue has been unified unified to @var{Term}, the
|
a term from the queue has been unified unified to @var{Term}, the
|
||||||
term is deleted from the queue and this predicate returns.
|
term is deleted from the queue and this predicate returns.
|
||||||
|
|
||||||
Please note that not-unifying messages remain in the queue. After
|
Please note that not-unifying messages remain in the queue. After
|
||||||
the following has been executed, thread 1 has the term \term{b}{gnu}
|
the following has been executed, thread 1 has the term @code{gnu}
|
||||||
in its queue and continues execution using @var{A} is @code{gnat}.
|
in its queue and continues execution using @var{A} is @code{gnat}.
|
||||||
|
|
||||||
\begin{code}
|
@example
|
||||||
<thread 1>
|
<thread 1>
|
||||||
thread_get_message(a(A)),
|
thread_get_message(a(A)),
|
||||||
|
|
||||||
<thread 2>
|
<thread 2>
|
||||||
thread_send_message(b(gnu)),
|
thread_send_message(b(gnu)),
|
||||||
thread_send_message(a(gnat)),
|
thread_send_message(a(gnat)),
|
||||||
\end{code}
|
@end example
|
||||||
|
|
||||||
See also thread_peek_message/1.
|
See also @code{thread_peek_message/1}.
|
||||||
|
|
||||||
\predicate{thread_peek_message}{1}{?Term}
|
@item thread_peek_message(?@var{Term})
|
||||||
|
@findex thread_peek_message/1
|
||||||
|
@snindex thread_peek_message/1
|
||||||
|
@cnindex thread_peek_message/1
|
||||||
Examines the thread message-queue and compares the queued terms
|
Examines the thread message-queue and compares the queued terms
|
||||||
with @var{Term} until one unifies or the end of the queue has been
|
with @var{Term} until one unifies or the end of the queue has been
|
||||||
reached. In the first case the call succeeds (possibly instantiating
|
reached. In the first case the call succeeds (possibly instantiating
|
||||||
@var{Term}. If no term from the queue unifies this call fails.
|
@var{Term}. If no term from the queue unifies this call fails.
|
||||||
|
|
||||||
\predicate{message_queue_create}{1}{?Queue}
|
@item thread_message_queue_create(?@var{Queue})
|
||||||
|
@findex thread_message_queue_create/1
|
||||||
|
@snindex thread_message_queue_create/1
|
||||||
|
@cnindex thread_message_queue_create/1
|
||||||
If @var{Queue} is an atom, create a named queue. To avoid ambiguity
|
If @var{Queue} is an atom, create a named queue. To avoid ambiguity
|
||||||
of thread_send_message/2, the name of a queue may not be in use as a
|
on @code{thread_send_message/2}, the name of a queue may not be in use
|
||||||
thread-name. If @var{Queue} is unbound an anonymous queue is created
|
as a thread-name. If @var{Queue} is unbound an anonymous queue is
|
||||||
and @var{Queue} is unified to its identifier.
|
created and @var{Queue} is unified to its identifier.
|
||||||
|
|
||||||
\predicate{message_queue_destroy}{1}{+Queue}
|
@item thread_message_queue_destroy(+@var{Queue})
|
||||||
|
@findex thread_message_queue_destroy/1
|
||||||
|
@snindex thread_message_queue_destroy/1
|
||||||
|
@cnindex thread_message_queue_destroy/1
|
||||||
Destroy a message queue created with message_queue_create/1. It is
|
Destroy a message queue created with message_queue_create/1. It is
|
||||||
\emph{not} allows to destroy the queue of a thread. Neither is it
|
@emph{not} allows to destroy the queue of a thread. Neither is it
|
||||||
allowed to destroy a queue other threads are waiting for or, for
|
allowed to destroy a queue other threads are waiting for or, for
|
||||||
anynymous message queues, may try to wait for later.%
|
anynymous message queues, may try to wait for later.%
|
||||||
\bug{None of these constraints are properly enforced by the
|
|
||||||
system in the current implementation. It is therefore
|
|
||||||
advised not to delete queues unless you are absolutely
|
|
||||||
sure it is safe.}
|
|
||||||
|
|
||||||
\predicate{thread_get_message}{2}{+Queue, ?Term}
|
@item thread_get_message(+@var{Queue}, +@var{Term})
|
||||||
As thread_get_message/1, operating on a given queue. It is allowed (but
|
@findex thread_get_message/2
|
||||||
not advised) to get messages from the queue of other threads.
|
@snindex thread_get_message/2
|
||||||
|
@cnindex thread_get_message/2
|
||||||
\predicate{thread_get_message}{2}{+Queue, ?Term}
|
|
||||||
As thread_get_message/1, operating on a given queue. It is allowed to
|
As thread_get_message/1, operating on a given queue. It is allowed to
|
||||||
peek into another thread's message queue, an operation that can be used
|
peek into another thread's message queue, an operation that can be used
|
||||||
to check whether a thread has swallowed a message sent to it.
|
to check whether a thread has swallowed a message sent to it.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
|
||||||
Explicit message queues are designed with the \jargon{worker-pool} model
|
Explicit message queues are designed with the @emph{worker-pool} model
|
||||||
in mind, where multiple threads wait on a single queue and pick up the
|
in mind, where multiple threads wait on a single queue and pick up the
|
||||||
first goal to execute. Below is a simple implementation where the
|
first goal to execute. Below is a simple implementation where the
|
||||||
workers execute arbitrary Prolog goals. Note that this example provides
|
workers execute arbitrary Prolog goals. Note that this example provides
|
||||||
no means to tell when all work is done. This must be realised using
|
no means to tell when all work is done. This must be realised using
|
||||||
additional synchronisation.
|
additional synchronisation.
|
||||||
|
|
||||||
\begin{code}
|
@example
|
||||||
% create_workers(+Id, +N)
|
% create_workers(+Id, +N)
|
||||||
%
|
%
|
||||||
% Create a pool with given Id and number of workers.
|
% Create a pool with given Id and number of workers.
|
||||||
@ -12476,26 +12496,29 @@ do_work(Id) :-
|
|||||||
|
|
||||||
work(Id, Goal) :-
|
work(Id, Goal) :-
|
||||||
thread_send_message(Id, Goal).
|
thread_send_message(Id, Goal).
|
||||||
\end{code}
|
@end example
|
||||||
|
|
||||||
|
@node Signalling Threads, Threads and Dynamic Predicates,Message Queues, Thread Communication
|
||||||
\subsection{Signalling threads}
|
@subsection Signalling Threads
|
||||||
|
|
||||||
These predicates provide a mechanism to make another thread execute some
|
These predicates provide a mechanism to make another thread execute some
|
||||||
goal as an \jargon{interrupt}. Signalling threads is safe as these
|
goal as an @emph{interrupt}. Signalling threads is safe as these
|
||||||
interrupts are only checked at safe points in the virtual machine.
|
interrupts are only checked at safe points in the virtual machine.
|
||||||
Nevertheless, signalling in multi-threaded environments should be
|
Nevertheless, signalling in multi-threaded environments should be
|
||||||
handled with care as the receiving thread may hold a \jargon{mutex}
|
handled with care as the receiving thread may hold a @emph{mutex}
|
||||||
(see with_mutex). Signalling probably only makes sense to start
|
(see with_mutex). Signalling probably only makes sense to start
|
||||||
debugging threads and to cancel no-longer-needed threads with throw/1,
|
debugging threads and to cancel no-longer-needed threads with @code{throw/1},
|
||||||
where the receiving thread should be designed carefully do handle
|
where the receiving thread should be designed carefully do handle
|
||||||
exceptions at any point.
|
exceptions at any point.
|
||||||
|
|
||||||
@table @code
|
@table @code
|
||||||
\predicate{thread_signal}{2}{+ThreadId, :Goal}
|
@item thread_signal(+@var{ThreadId}, :@var{Goal})
|
||||||
|
@findex thread_signal/2
|
||||||
|
@snindex thread_signal/2
|
||||||
|
@cnindex thread_signal/2
|
||||||
Make thread @var{ThreadId} execute @var{Goal} at the first
|
Make thread @var{ThreadId} execute @var{Goal} at the first
|
||||||
opportunity. In the current implementation, this implies at the first
|
opportunity. In the current implementation, this implies at the first
|
||||||
pass through the \jargon{Call-port}. The predicate thread_signal/2
|
pass through the @emph{Call-port}. The predicate @code{thread_signal/2}
|
||||||
itself places @var{Goal} into the signalled-thread's signal queue
|
itself places @var{Goal} into the signalled-thread's signal queue
|
||||||
and returns immediately.
|
and returns immediately.
|
||||||
|
|
||||||
@ -12504,43 +12527,46 @@ multi-threading, mainly because the status of mutexes cannot be
|
|||||||
guaranteed easily. At the call-port, the Prolog virtual machine
|
guaranteed easily. At the call-port, the Prolog virtual machine
|
||||||
holds no locks and therefore the asynchronous execution is safe.
|
holds no locks and therefore the asynchronous execution is safe.
|
||||||
|
|
||||||
@var{Goal} can be any valid Prolog goal, including throw/1 to make
|
@var{Goal} can be any valid Prolog goal, including @code{throw/1} to make
|
||||||
the receiving thread generate an exception and trace/0 to start
|
the receiving thread generate an exception and @code{trace/0} to start
|
||||||
tracing the receiving thread.
|
tracing the receiving thread.
|
||||||
|
|
||||||
In the Windows version, the receiving thread immediately executes
|
@comment In the Windows version, the receiving thread immediately executes
|
||||||
the signal if it reaches a Windows GetMessage() call, which generally
|
@comment the signal if it reaches a Windows GetMessage() call, which generally
|
||||||
happens of the thread is waiting for (user-)input.
|
@comment happens of the thread is waiting for (user-)input.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
@node Threads and Dynamic Predicates, , Signalling Threads, Thread Communication
|
||||||
|
@subsection Threads and Dynamic Predicates
|
||||||
|
|
||||||
\subsection{Threads and dynamic predicates} \label{sec:threadlocal}
|
Besides queues threads can share and exchange data using dynamic
|
||||||
|
predicates. The multi-threaded version knows about two types of
|
||||||
Besides queues (\secref{msgqueue}) threads can share and exchange
|
dynamic predicates. By default, a predicate declared @emph{dynamic}
|
||||||
data using dynamic predicates. The multi-threaded version knows about
|
(see @code{dynamic/1}) is shared by all threads. Each thread may
|
||||||
two types of dynamic predicates. By default, a predicate declared
|
assert, retract and run the dynamic predicate. Synchronisation inside
|
||||||
\jargon{dynamic} (see dynamic/1) is shared by all threads. Each thread
|
Prolog guarantees the consistency of the predicate. Updates are
|
||||||
may assert, retract and run the dynamic predicate. Synchronisation
|
@emph{logical}: visible clauses are not affected by assert/retract
|
||||||
inside Prolog guarantees the consistency of the predicate. Updates are
|
|
||||||
\jargon{logical}: visible clauses are not affected by assert/retract
|
|
||||||
after a query started on the predicate. In many cases primitive from
|
after a query started on the predicate. In many cases primitive from
|
||||||
\secref{threadsync} should be used to ensure application invariants on
|
thread synchronysation should be used to ensure application invariants on
|
||||||
the predicate are maintained.
|
the predicate are maintained.
|
||||||
|
|
||||||
Besides shared predicates, dynamic predicates can be declared with the
|
Besides shared predicates, dynamic predicates can be declared with the
|
||||||
thread_local/1 directive. Such predicates share their attributes, but
|
@code{thread_local/1} directive. Such predicates share their
|
||||||
the clause-list is different in each thread.
|
attributes, but the clause-list is different in each thread.
|
||||||
|
|
||||||
@table @code
|
@table @code
|
||||||
\prefixop{thread_local}{+Functor/+Arity, \ldots}
|
@item thread_local(@var{+Functor/Arity})
|
||||||
This directive is related to the dynamic/1 directive. It tells the
|
@findex thread_local/1 (directive)
|
||||||
system that the predicate may be modified using assert/1, retract/1,
|
@snindex thread_local/1 (directive)
|
||||||
etc.\ during execution of the program. Unlike normal shared dynamic
|
@cnindex thread_local/1 (directive)
|
||||||
|
related to the dynamic/1 directive. It tells the system that the
|
||||||
|
predicate may be modified using @code{assert/1}, @code{retract/1},
|
||||||
|
etc, during execution of the program. Unlike normal shared dynamic
|
||||||
data however each thread has its own clause-list for the predicate.
|
data however each thread has its own clause-list for the predicate.
|
||||||
As a thread starts, this clause list is empty. If there are still
|
As a thread starts, this clause list is empty. If there are still
|
||||||
clauses as the thread terminates these are automatically reclaimed
|
clauses as the thread terminates these are automatically reclaimed by
|
||||||
by the system (see also volatile/1). The thread_local property
|
the system. The thread_local property implies
|
||||||
implies the properties dynamic and volatile.
|
the property dynamic.
|
||||||
|
|
||||||
Thread-local dynamic predicates are intended for maintaining
|
Thread-local dynamic predicates are intended for maintaining
|
||||||
thread-specific state or intermediate results of a computation.
|
thread-specific state or intermediate results of a computation.
|
||||||
@ -12550,34 +12576,31 @@ a file as in the example below as the clause is only visible from the
|
|||||||
thread that loaded the source-file. All other threads start with an
|
thread that loaded the source-file. All other threads start with an
|
||||||
empty clause-list.
|
empty clause-list.
|
||||||
|
|
||||||
\begin{code}
|
@example
|
||||||
:- thread_local
|
:- thread_local
|
||||||
foo/1.
|
foo/1.
|
||||||
|
|
||||||
foo(gnat).
|
foo(gnat).
|
||||||
\end{code}
|
@end example
|
||||||
|
|
||||||
\textbf{DISCLAIMER} Whether or not this declaration is apropriate in
|
|
||||||
the sense of the proper mechanism to reach the goal is still debated.
|
|
||||||
If you have strong feeling in favour or against, please share them
|
|
||||||
in the SWI-Prolog mailing list.
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
|
||||||
\section{Thread synchronisation} \label{sec:threadsync}
|
@node Thread Synchronisation, , Thread Communication, Threads
|
||||||
|
@section Thread Synchronisation
|
||||||
|
|
||||||
All internal Prolog operations are thread-safe. This implies two Prolog
|
All internal Prolog operations are thread-safe. This implies two Prolog
|
||||||
threads can operate on the same dynamic predicate without corrupting the
|
threads can operate on the same dynamic predicate without corrupting the
|
||||||
consistency of the predicate. This section deals with user-level
|
consistency of the predicate. This section deals with user-level
|
||||||
\jargon{mutexes} (called \jargon{monitors} in ADA or
|
@emph{mutexes} (called @emph{monitors} in ADA or
|
||||||
\jargon{critical-sections} by Microsoft). A mutex is a
|
@emph{critical-sections} by Microsoft). A mutex is a
|
||||||
{\bf MUT}ual {\bf EX}clusive device, which implies at most one thread
|
@emph{MUT}ual @emph{EX}clusive device, which implies at most one thread
|
||||||
can \jargon{hold} a mutex.
|
can @emph{hold} a mutex.
|
||||||
|
|
||||||
Mutexes are used to realise related updates to the Prolog database.
|
Mutexes are used to realise related updates to the Prolog database.
|
||||||
With `related', we refer to the situation where a `transaction' implies
|
With `related', we refer to the situation where a `transaction' implies
|
||||||
two or more changes to the Prolog database. For example, we have a
|
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
|
predicate @code{address/2}, representing the address of a person and we want
|
||||||
to change the address by retracting the old and asserting the new
|
to change the address by retracting the old and asserting the new
|
||||||
address. Between these two operations the database is invalid: this
|
address. Between these two operations the database is invalid: this
|
||||||
person has either no address or two addresses, depending on the
|
person has either no address or two addresses, depending on the
|
||||||
@ -12585,7 +12608,7 @@ assert/retract order.
|
|||||||
|
|
||||||
Here is how to realise a correct update:
|
Here is how to realise a correct update:
|
||||||
|
|
||||||
\begin{code}
|
@example
|
||||||
:- initialization
|
:- initialization
|
||||||
mutex_create(addressbook).
|
mutex_create(addressbook).
|
||||||
|
|
||||||
@ -12594,63 +12617,86 @@ change_address(Id, Address) :-
|
|||||||
retractall(address(Id, _)),
|
retractall(address(Id, _)),
|
||||||
asserta(address(Id, Address)),
|
asserta(address(Id, Address)),
|
||||||
mutex_unlock(addressbook).
|
mutex_unlock(addressbook).
|
||||||
\end{code}
|
@end example
|
||||||
|
|
||||||
|
|
||||||
@table @code
|
@table @code
|
||||||
\predicate{mutex_create}{1}{?MutexId}
|
@item mutex_create(?@var{MutexId})
|
||||||
Create a mutex. if @var{MutexId} is an atom, a \jargon{named} mutex is
|
@findex mutex_create/1
|
||||||
|
@snindex mutex_create/1
|
||||||
|
@cnindex mutex_create/1
|
||||||
|
Create a mutex. if @var{MutexId} is an atom, a @emph{named} mutex is
|
||||||
created. If it is a variable, an anonymous mutex reference is returned.
|
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.
|
There is no limit to the number of mutexes that can be created.
|
||||||
|
|
||||||
\predicate{mutex_destroy}{1}{+MutexId}
|
@item mutex_destroy(+@var{MutexId})
|
||||||
|
@findex mutex_destroy/1
|
||||||
|
@snindex mutex_destroy/1
|
||||||
|
@cnindex mutex_destroy/1
|
||||||
Destroy a mutex. After this call, @var{MutexId} becomes invalid and
|
Destroy a mutex. After this call, @var{MutexId} becomes invalid and
|
||||||
further references yield an \except{existence_error} exception.
|
further references yield an @code{existence_error} exception.
|
||||||
|
|
||||||
\predicate{with_mutex}{2}{+MutexId, :Goal}
|
@item with_mutex(+@var{MutexId}, :@var{Goal})
|
||||||
|
@findex with_mutex/2
|
||||||
|
@snindex with_mutex/2
|
||||||
|
@cnindex with_mutex/2
|
||||||
Execute @var{Goal} while holding @var{MutexId}. If @var{Goal} leaves
|
Execute @var{Goal} while holding @var{MutexId}. If @var{Goal} leaves
|
||||||
choicepointes, these are destroyed (as in once/1). The mutex is unlocked
|
choicepoints, these are destroyed (as in @code{once/1}). The mutex is unlocked
|
||||||
regardless of whether @var{Goal} succeeds, fails or raises an exception.
|
regardless of whether @var{Goal} succeeds, fails or raises an exception.
|
||||||
An exception thrown by @var{Goal} is re-thrown after the mutex has been
|
An exception thrown by @var{Goal} is re-thrown after the mutex has been
|
||||||
successfully unlocked. See also mutex_create/2 and call_cleanup/3.
|
successfully unlocked. See also @code{mutex_create/2}.
|
||||||
|
|
||||||
Although described in the thread-section, this predicate is also
|
Although described in the thread-section, this predicate is also
|
||||||
available in the single-threaded version, where it behaves simply as
|
available in the single-threaded version, where it behaves simply as
|
||||||
once/1.
|
once/1.
|
||||||
|
|
||||||
\predicate{mutex_lock}{1}{+MutexId}
|
@item mutex_lock(+@var{MutexId})
|
||||||
Lock the mutex. Prolog mutexes are \jargon{recursive} mutexes: they
|
@findex mutex_lock/1
|
||||||
|
@snindex mutex_lock/1
|
||||||
|
@cnindex mutex_lock/1
|
||||||
|
Lock the mutex. Prolog mutexes are @emph{recursive} mutexes: they
|
||||||
can be locked multiple times by the same thread. Only after unlocking
|
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
|
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
|
locking by other threads. If another thread has locked the mutex the
|
||||||
calling thread is suspended until to mutex is unlocked.
|
calling thread is suspended until to mutex is unlocked.
|
||||||
|
|
||||||
If @var{MutexId} is an atom, and there is no current mutex with that
|
If @var{MutexId} is an atom, and there is no current mutex with that
|
||||||
name, the mutex is created automatically using mutex_create/1. This
|
name, the mutex is created automatically using @code{mutex_create/1}. This
|
||||||
implies named mutexes need not be declared explicitly.
|
implies named mutexes need not be declared explicitly.
|
||||||
|
|
||||||
Please note that locking and unlocking mutexes should be paired
|
Please note that locking and unlocking mutexes should be paired
|
||||||
carefully. Especially make sure to unlock mutexes even if the protected
|
carefully. Especially make sure to unlock mutexes even if the protected
|
||||||
code fails or raises an exception. For most common cases use
|
code fails or raises an exception. For most common cases use
|
||||||
with_mutex/2, wich provides a safer way for handling prolog-level
|
@code{with_mutex/2}, wich provides a safer way for handling prolog-level
|
||||||
mutexes. The predicate call_cleanup/[2-3] is another way to guarantee
|
mutexes.
|
||||||
that the mutex is unlocked while retaining non-determinism.
|
|
||||||
|
|
||||||
\predicate{mutex_trylock}{1}{+MutexId}
|
@item mutex_trylock(+@var{MutexId})
|
||||||
|
@findex mutex_trylock/1
|
||||||
|
@snindex mutex_trylock/1
|
||||||
|
@cnindex mutex_trylock/1
|
||||||
As mutex_lock/1, but if the mutex is held by another thread, this
|
As mutex_lock/1, but if the mutex is held by another thread, this
|
||||||
predicates fails immediately.
|
predicates fails immediately.
|
||||||
|
|
||||||
\predicate{mutex_unlock}{1}{+MutexId}
|
@item mutex_unlock(+@var{MutexId})
|
||||||
|
@findex mutex_unlock/1
|
||||||
|
@snindex mutex_unlock/1
|
||||||
|
@cnindex mutex_unlock/1
|
||||||
Unlock the mutex. This can only be called if the mutex is held by the
|
Unlock the mutex. This can only be called if the mutex is held by the
|
||||||
calling thread. If this is not the case, a \except{permission_error}
|
calling thread. If this is not the case, a @code{permission_error}
|
||||||
exception is raised.
|
exception is raised.
|
||||||
|
|
||||||
\predicate{mutex_unlock_all}{0}{}
|
@item mutex_unlock_all
|
||||||
|
@findex mutex_unlock_all/0
|
||||||
|
@snindex mutex_unlock_all/0
|
||||||
|
@cnindex mutex_unlock_all/0
|
||||||
Unlock all mutexes held by the current thread. This call is especially
|
Unlock all mutexes held by the current thread. This call is especially
|
||||||
useful to handle thread-termination using abort/0 or exceptions. See
|
useful to handle thread-termination using @code{abort/0} or exceptions. See
|
||||||
also thread_signal/2.
|
also @code{thread_signal/2}.
|
||||||
|
|
||||||
\predicate{current_mutex}{3}{?MutexId, ?ThreadId, ?Count}
|
@item current_mutex(?@var{MutexId}, ?@var{ThreadId}, ?@var{Count})
|
||||||
|
@findex current_mutex/3
|
||||||
|
@snindex current_mutex/3
|
||||||
|
@cnindex current_mutex/3
|
||||||
Enumerates all existing mutexes. If the mutex is held by some thread,
|
Enumerates all existing mutexes. If the mutex is held by some thread,
|
||||||
@var{ThreadId} is unified with the identifier of te holding thread and
|
@var{ThreadId} is unified with the identifier of te holding thread and
|
||||||
@var{Count} with the recursive count of the mutex. Otherwise,
|
@var{Count} with the recursive count of the mutex. Otherwise,
|
||||||
@ -12658,8 +12704,7 @@ Enumerates all existing mutexes. If the mutex is held by some thread,
|
|||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
|
||||||
|
@node Parallelism, Tabling, Threads, Extensions
|
||||||
@node Parallelism, Tabling, Logtalk, Extensions
|
|
||||||
@chapter Parallelism
|
@chapter Parallelism
|
||||||
|
|
||||||
@cindex parallelism
|
@cindex parallelism
|
||||||
|
Reference in New Issue
Block a user