fix missing ClSize in index.c
start introducing docs for threads (copied from SWI). git-svn-id: https://yap.svn.sf.net/svnroot/yap/trunk@1019 b08c6af1-5177-4d33-ba66-4b1c6b8b522a
This commit is contained in:
parent
caa0b26665
commit
ad685b6e5b
18
C/absmi.c
18
C/absmi.c
@ -113,6 +113,15 @@ push_live_regs(yamop *pco)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LOW_PROF
|
||||
#include <stdio.h>
|
||||
void prof_alrm(int signo)
|
||||
{
|
||||
fprintf(FProf,"%p\n", PREG);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
Int
|
||||
Yap_absmi(int inp)
|
||||
{
|
||||
@ -11751,12 +11760,3 @@ Yap_absmi(int inp)
|
||||
}
|
||||
|
||||
|
||||
#if LOW_PROF
|
||||
#include <stdio.h>
|
||||
void prof_alrm(int signo)
|
||||
{
|
||||
fprintf(FProf,"%p\n", PREG);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -383,7 +383,7 @@ decrease_log_indices(LogUpdIndex *c, yamop *suspend_code)
|
||||
beg->u.Ill.l1 != beg->u.Ill.l2) {
|
||||
end = beg->u.Ill.l2;
|
||||
} else {
|
||||
end = (yamop *)((CODEADDR)c+c->ClSize);
|
||||
end = (yamop *)((CODEADDR)c+c->ClSize);
|
||||
}
|
||||
ipc = beg;
|
||||
cleanup_dangling_indices(ipc, beg, end, suspend_code);
|
||||
|
@ -630,7 +630,7 @@ init_dbtable(tr_fr_ptr trail_ptr) {
|
||||
}
|
||||
} else {
|
||||
DynamicClause *dcl = ClauseFlagsToDynamicClause(pt0);
|
||||
store_in_dbtable((CODEADDR)dcl, (CODEADDR)dcl->ClSize, dcl_entry);
|
||||
store_in_dbtable((CODEADDR)dcl, (CODEADDR)dcl+dcl->ClSize, dcl_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4867,6 +4867,7 @@ replace_lu_block(LogUpdIndex *blk, int flag, PredEntry *ap, yamop *code, int has
|
||||
ncl->ClRefCount = 0;
|
||||
ncl->u.ParentIndex = blk->u.ParentIndex;
|
||||
ncl->ChildIndex = NULL;
|
||||
ncl->ClSize = sz;
|
||||
INIT_LOCK(ncl->ClLock);
|
||||
INIT_CLREF_COUNT(ncl);
|
||||
codep = start = ncl->ClCode;
|
||||
|
533
docs/yap.tex
533
docs/yap.tex
@ -113,6 +113,11 @@ by LMU.
|
||||
|
||||
Copyright @copyright{} 1996-98 LMU (Ludwig-Maximilians-University)@*
|
||||
Munich, Germany
|
||||
|
||||
This file contains extracts of the SWI-Prolog manual, as written by Jan
|
||||
Wielemaker. Our thanks to the author for his kind permission in allowing
|
||||
us to include his text in this document.
|
||||
|
||||
@menu
|
||||
* Intro:: Introduction
|
||||
* Install:: Installation
|
||||
@ -129,6 +134,7 @@ Munich, Germany
|
||||
* CLPQR:: The CLP(Q,R) System
|
||||
* CHR:: The CHR System
|
||||
* Logtalk:: The Logtalk Object-Oriented System
|
||||
* Threads:: Thread Library
|
||||
* Parallelism:: Running in Or-Parallel
|
||||
* Tabling:: Storing Intermediate Solutions of programs
|
||||
* Low Level Profiling:: Profiling Abstract Machine Instructions
|
||||
@ -8738,6 +8744,7 @@ Extensions to Traditional Prolog
|
||||
* Attributed Variables:: Using attributed Variables
|
||||
* CLPQR:: The CLP(Q,R) System
|
||||
* Logtalk:: The Logtalk Object-Oriented system
|
||||
* Threads:: Thread Library
|
||||
* Parallelism:: Running in Or-Parallel
|
||||
* Tabling:: Storing Intermediate Solutions of programs
|
||||
* Low Level Profiling:: Profiling Abstract Machine Instructions
|
||||
@ -12103,7 +12110,7 @@ debugging environment is not available in SICStus Prolog.
|
||||
@end enumerate
|
||||
|
||||
|
||||
@node Logtalk, Parallelism, CHR, Extensions
|
||||
@node Logtalk, Threads, CHR, Extensions
|
||||
@chapter Logtalk
|
||||
@cindex logtalk
|
||||
|
||||
@ -12116,6 +12123,530 @@ modules in its implementation.
|
||||
Logtalk documentation is included in the Logtalk directory. For the
|
||||
latest news, please see the URL @url{http://www.logtalk.org/}.
|
||||
|
||||
@node Threads, Parallelism, Logtalk, Extensions
|
||||
@chapter Parallelism
|
||||
|
||||
YAP implements a SWI-Prolog compatible multithreading
|
||||
library. Like in SWI-Prolog, Prolog threads have their own stacks and
|
||||
only share the Prolog \emph{heap}: predicates, records, flags and other
|
||||
global non-backtrackable data. The package is based on the POSIX thread
|
||||
standard \cite{Butenhof:1997:PPT} used on most popular systems except
|
||||
for MS-Windows.
|
||||
|
||||
@comment On Windows it uses the
|
||||
@comment \url[pthread-win32]{http://sources.redhat.com/pthreads-win32/} emulation
|
||||
@comment of POSIX threads mixed with the Windows native API for smoother and
|
||||
@comment faster operation.
|
||||
|
||||
@menu
|
||||
Subnodes of Threads
|
||||
* Creating and destroying Prolog threads::
|
||||
* Monitoring threads::
|
||||
* Thread communication::
|
||||
* Thread synchronisation::
|
||||
|
||||
@end menu
|
||||
|
||||
\node Creating and destroying Prolog threads, , ,Threads
|
||||
@section Creating and destroying Prolog threads
|
||||
|
||||
@table @code
|
||||
|
||||
@item thread_create(:@var{Goal}, -@var{Id}, +@var{Options})
|
||||
@findex thread_create/3
|
||||
@snindex thread_create/3
|
||||
@cnindex thread_create/3
|
||||
|
||||
Create a new Prolog thread (and underlying C-thread) and start it
|
||||
by executing @var{Goal}. If the thread is created succesfully, the
|
||||
thread-identifier of the created thread is unified to @var{Id}.
|
||||
@var{Options} is a list of options. Currently defined options are:
|
||||
|
||||
@table @code
|
||||
@item{stack}
|
||||
Set the limit in K-Bytes to which the local stack of this thread may grow. If
|
||||
omited, the limit of the calling thread is used. See also the
|
||||
\cmdlineoption{-s} commandline option.
|
||||
|
||||
@item{trail}
|
||||
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
|
||||
\cmdlineoption{-t} commandline option.
|
||||
|
||||
@item{stack}
|
||||
Set the limit in K-Bytes to which the system stack of this thread may
|
||||
grow. The default, mimimum and maximum values are system-dependant.
|
||||
|
||||
@item{alias}
|
||||
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 @code{thread_join/2}).
|
||||
|
||||
@item{detached}
|
||||
If @code{false} (default), the thread can be waited for using
|
||||
@code{thread_join/2}. @code{thread_join/2} must be called on this thread
|
||||
to reclaim the all resources associated to the thread. If @code{true},
|
||||
the system will reclaim all associated resources automatically after the
|
||||
thread finishes. Please note that thread identifiers are freed for reuse
|
||||
after a detached thread finishes or a normal thread has been joined.
|
||||
See also @code{thread_join/2} and @code{thread_detach/1}.
|
||||
@end table
|
||||
|
||||
The @var{Goal} argument is \emph{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.
|
||||
|
||||
@item thread_self(-@var{Id})
|
||||
@findex thread_self/1
|
||||
@snindex thread_self/1
|
||||
@cnindex thread_self/1
|
||||
Get the Prolog thread identifier of the running thread. If the thread
|
||||
has an alias, the alias-name is returned.
|
||||
|
||||
@item thread_join(+@var{Id}, -@var{Status})
|
||||
@findex thread_join/2
|
||||
@snindex thread_join/2
|
||||
@cnindex thread_join/2
|
||||
Wait for the termination of thread with given @var{Id}. Then unify the
|
||||
result-status of the thread with @var{Status}. After this call,
|
||||
@var{Id} becomes invalid and all resources associated with the thread
|
||||
are reclaimed. Note that threads with the attribute @code{detached}
|
||||
@code{true} cannot be joined. See also @code{current_thread/2}.
|
||||
|
||||
A thread that has been completed without @code{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 representing the
|
||||
exit-status of the thread is retained until @code{thread_join/2} is called on
|
||||
the thread. Defined values for @var{Status} are:
|
||||
|
||||
@table @code
|
||||
@item{true}{}
|
||||
The goal has been proven successfully.
|
||||
|
||||
@item{false}{}
|
||||
The goal has failed.
|
||||
|
||||
@item{exception}{@var{Term}} The thread is terminated on an
|
||||
exception. See @code{print_message/2} to turn system exceptions into
|
||||
readable messages.
|
||||
|
||||
@item{exited}{@var{Term}}
|
||||
The thread is terminated on thread_exit/1 using the argument @var{Term}.
|
||||
@end table
|
||||
|
||||
|
||||
@item thread_detach(+@var{Id})
|
||||
@findex thread_detach/1
|
||||
@snindex thread_detach/1
|
||||
@cnindex thread_detach/1
|
||||
Switch thread into detached-state (see \code{detached} option at
|
||||
@code{thread_create/3} at runtime. @var{Id} is the identifier of the thread
|
||||
placed in detached state.
|
||||
|
||||
One of the possible applications is to simplify debugging. Threads that
|
||||
are created as @code{detached} leave no traces if they crash. For
|
||||
not-detached threads the status can be inspected using
|
||||
@code{current_thread/2}. Threads nobody is waiting for may be created
|
||||
normally and detach themselves just before completion. This way they
|
||||
leave no traces on normal completion and their reason for failure can be
|
||||
inspected.
|
||||
|
||||
@item thread_exit(+@var{Term})
|
||||
@findex thread_exit/1
|
||||
@snindex thread_exit/1
|
||||
@cnindex thread_exit/1
|
||||
Terminates the thread immediately, leaving @code{exited(@var{Term})} as
|
||||
result-state for @code{thread_join/2}. If the thread has the attribute
|
||||
\term{detached}{true} it terminates, but its exit status cannot be
|
||||
retrieved using @code{thread_join/2} making the value of @var{Term}
|
||||
irrelevant. The Prolog stacks and C-thread are reclaimed.
|
||||
|
||||
\predicate{thread_at_exit}{1}{:Goal}
|
||||
Run @var{Goal} just before releasing the thread resources. 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 using the result of thread_self/1 as thread-identifier.
|
||||
|
||||
\predicate{thread_setconcurrency}{2}{-Old, +New}
|
||||
\index{Solaris}%
|
||||
Determine the concurrency of the process, which is defined as the
|
||||
maximum number of concurrently active threads. `Active' here means they
|
||||
are using CPU time. This option is provided if the thread-implementation
|
||||
provides pthread_setconcurrency(). Solaris is a typical example of this
|
||||
family. On other systems this predicate unifies @var{Old} to 0 (zero)
|
||||
and succeeds silently.
|
||||
@end table
|
||||
|
||||
|
||||
\section{Monitoring threads} \label{sec:thmonitor}
|
||||
|
||||
Normal multi-threaded applications should not need these the predicates
|
||||
from this section because almost any usage of these predicates is
|
||||
unsafe. For example checking the existence of a thread before signalling
|
||||
it is of no use as it may vanish between the two calls. Catching
|
||||
exceptions using catch/3 is the only safe way to deal with
|
||||
thread-existence errors.
|
||||
|
||||
These predicates are provided for diagnosis and monitoring tasks. See
|
||||
also \secref{thutil}, describing more high-level primitives.
|
||||
|
||||
|
||||
@table @code
|
||||
\predicate{current_thread}{2}{?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 @var{Id} instead of the numerical thread identifier.
|
||||
@var{Status} is one of:
|
||||
|
||||
@table @code
|
||||
@item{running}{}
|
||||
The thread is running. This is the initial status of a thread. Please
|
||||
note that threads waiting for something are considered running too.
|
||||
|
||||
@item{false}{}
|
||||
The @var{Goal} of the thread has been completed and failed.
|
||||
|
||||
@item{true}{}
|
||||
The @var{Goal} of the thread has been completed and succeeded.
|
||||
|
||||
@item{exited}{Term}
|
||||
The @var{Goal} of the thread has been terminated using thread_exit/1
|
||||
with @var{Term} as argument. If the underlying native thread has
|
||||
exited (using pthread_exit()) @var{Term} is unbound.
|
||||
|
||||
@item{exception}{Term}
|
||||
The @var{Goal} of the thread has been terminated due to an uncaught
|
||||
exception (see throw/1 and catch/3).
|
||||
@end table
|
||||
|
||||
\predicate{thread_statistics}{3}{+Id, +Key, -Value}
|
||||
Obtains statistical information on thread @var{Id} as statistics/2
|
||||
does in single-threaded applications. This call returns all keys
|
||||
of statistics/2, although only information statistics about the
|
||||
stacks and CPU time yield different values for each thread.%
|
||||
\footnote{Getting the CPU-time of a different thread is
|
||||
not supported on all platforms. For Microsoft,
|
||||
it does not work in 95/98/ME. For POSIX systems
|
||||
it requires times() to return values specific for
|
||||
the calling thread.}
|
||||
|
||||
\predicate{mutex_statistics}{0}{}
|
||||
Print usage statistics on internal mutexes and mutexes associated
|
||||
with dynamic predicates. For each mutex two numbers are printed:
|
||||
the number of times the mutex was acquired and the number of
|
||||
\jargon{collisions}: the number times the calling thread has to
|
||||
wait for the mutex. The collistion-count is not available on
|
||||
Windows as this would break portability to Windows-95/98/ME or
|
||||
significantly harm performance. Generally collision count is
|
||||
close to zero on single-CPU hardware.
|
||||
@end table
|
||||
|
||||
|
||||
\section{Thread communication} \label{sec:threadcom}
|
||||
|
||||
\subsection{Message queues} \label{sec:msgqueue}
|
||||
|
||||
Prolog threads can exchange data using dynamic predicates, database
|
||||
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
|
||||
expensive polling loop. \jargon{Message queues} provide a means for
|
||||
threads to wait for data or conditions without using the CPU.
|
||||
|
||||
Each thread has a message-queue attached to it that is identified
|
||||
by the thread. Additional queues are created using
|
||||
message_queue_create/2.
|
||||
|
||||
@table @code
|
||||
\predicate{thread_send_message}{2}{+QueueOrThreadId, +Term}
|
||||
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_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
|
||||
thus lost. This call returns immediately.
|
||||
|
||||
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
|
||||
@var{Term}, the waiting threads are \emph{all} sent a wakeup signal,
|
||||
starting a rush for the available messages in the queue. This behaviour
|
||||
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
|
||||
there is only one waiting thread or all waiting threads wait with an
|
||||
unbound variable an arbitrary thread is restarted to scan the queue.%
|
||||
\footnote{See the documentation for the POSIX thread functions
|
||||
pthread_cond_signal() v.s.\ pthread_cond_broadcastt()
|
||||
for background information.}
|
||||
|
||||
\predicate{thread_get_message}{1}{?Term}
|
||||
Examines the thread message-queue and if necessary blocks execution
|
||||
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
|
||||
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 \term{b}{gnu}
|
||||
in its queue and continues execution using @var{A} is @code{gnat}.
|
||||
|
||||
\begin{code}
|
||||
<thread 1>
|
||||
thread_get_message(a(A)),
|
||||
|
||||
<thread 2>
|
||||
thread_send_message(b(gnu)),
|
||||
thread_send_message(a(gnat)),
|
||||
\end{code}
|
||||
|
||||
See also thread_peek_message/1.
|
||||
|
||||
\predicate{thread_peek_message}{1}{?Term}
|
||||
Examines the thread message-queue and compares the queued terms
|
||||
with @var{Term} until one unifies or the end of the queue has been
|
||||
reached. In the first case the call succeeds (possibly instantiating
|
||||
@var{Term}. If no term from the queue unifies this call fails.
|
||||
|
||||
\predicate{message_queue_create}{1}{?Queue}
|
||||
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
|
||||
thread-name. If @var{Queue} is unbound an anonymous queue is created
|
||||
and @var{Queue} is unified to its identifier.
|
||||
|
||||
\predicate{message_queue_destroy}{1}{+Queue}
|
||||
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
|
||||
allowed to destroy a queue other threads are waiting for or, for
|
||||
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}
|
||||
As thread_get_message/1, operating on a given queue. It is allowed (but
|
||||
not advised) to get messages from the queue of other threads.
|
||||
|
||||
\predicate{thread_get_message}{2}{+Queue, ?Term}
|
||||
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
|
||||
to check whether a thread has swallowed a message sent to it.
|
||||
@end table
|
||||
|
||||
|
||||
Explicit message queues are designed with the \jargon{worker-pool} model
|
||||
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
|
||||
workers execute arbitrary Prolog goals. Note that this example provides
|
||||
no means to tell when all work is done. This must be realised using
|
||||
additional synchronisation.
|
||||
|
||||
\begin{code}
|
||||
% create_workers(+Id, +N)
|
||||
%
|
||||
% Create a pool with given Id and number of workers.
|
||||
|
||||
create_workers(Id, N) :-
|
||||
message_queue_create(Id),
|
||||
forall(between(1, N, _),
|
||||
thread_create(do_work(Id), _, [])).
|
||||
|
||||
do_work(Id) :-
|
||||
repeat,
|
||||
thread_get_message(Id, Goal),
|
||||
( catch(Goal, E, print_message(error, E))
|
||||
-> true
|
||||
; print_message(error, goal_failed(Goal, worker(Id)))
|
||||
),
|
||||
fail.
|
||||
|
||||
% work(+Id, +Goal)
|
||||
%
|
||||
% Post work to be done by the pool
|
||||
|
||||
work(Id, Goal) :-
|
||||
thread_send_message(Id, Goal).
|
||||
\end{code}
|
||||
|
||||
|
||||
\subsection{Signalling threads}
|
||||
|
||||
These predicates provide a mechanism to make another thread execute some
|
||||
goal as an \jargon{interrupt}. Signalling threads is safe as these
|
||||
interrupts are only checked at safe points in the virtual machine.
|
||||
Nevertheless, signalling in multi-threaded environments should be
|
||||
handled with care as the receiving thread may hold a \jargon{mutex}
|
||||
(see with_mutex). Signalling probably only makes sense to start
|
||||
debugging threads and to cancel no-longer-needed threads with throw/1,
|
||||
where the receiving thread should be designed carefully do handle
|
||||
exceptions at any point.
|
||||
|
||||
@table @code
|
||||
\predicate{thread_signal}{2}{+ThreadId, :Goal}
|
||||
Make thread @var{ThreadId} execute @var{Goal} at the first
|
||||
opportunity. In the current implementation, this implies at the first
|
||||
pass through the \jargon{Call-port}. The predicate thread_signal/2
|
||||
itself places @var{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.
|
||||
|
||||
@var{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.
|
||||
|
||||
In the Windows version, the receiving thread immediately executes
|
||||
the signal if it reaches a Windows GetMessage() call, which generally
|
||||
happens of the thread is waiting for (user-)input.
|
||||
@end table
|
||||
|
||||
|
||||
\subsection{Threads and dynamic predicates} \label{sec:threadlocal}
|
||||
|
||||
Besides queues (\secref{msgqueue}) threads can share and exchange
|
||||
data using dynamic predicates. The multi-threaded version knows about
|
||||
two types of dynamic predicates. By default, a predicate declared
|
||||
\jargon{dynamic} (see dynamic/1) is shared by all threads. Each thread
|
||||
may assert, retract and run the dynamic predicate. Synchronisation
|
||||
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
|
||||
\secref{threadsync} should be used to ensure application invariants on
|
||||
the predicate are maintained.
|
||||
|
||||
Besides shared predicates, dynamic predicates can be declared with the
|
||||
thread_local/1 directive. Such predicates share their attributes, but
|
||||
the clause-list is different in each thread.
|
||||
|
||||
@table @code
|
||||
\prefixop{thread_local}{+Functor/+Arity, \ldots}
|
||||
This directive is related to the dynamic/1 directive. It tells the
|
||||
system that the predicate may be modified using assert/1, retract/1,
|
||||
etc.\ during execution of the program. Unlike normal shared dynamic
|
||||
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
|
||||
clauses as the thread terminates these are automatically reclaimed
|
||||
by the system (see also volatile/1). The thread_local property
|
||||
implies the properties dynamic and volatile.
|
||||
|
||||
Thread-local dynamic predicates are intended for maintaining
|
||||
thread-specific state or intermediate results of a computation.
|
||||
|
||||
It is not recommended to put clauses for a thread-local predicate into
|
||||
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
|
||||
empty clause-list.
|
||||
|
||||
\begin{code}
|
||||
:- thread_local
|
||||
foo/1.
|
||||
|
||||
foo(gnat).
|
||||
\end{code}
|
||||
|
||||
\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
|
||||
|
||||
|
||||
\section{Thread synchronisation} \label{sec:threadsync}
|
||||
|
||||
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
|
||||
\jargon{mutexes} (called \jargon{monitors} in ADA or
|
||||
\jargon{critical-sections} by Microsoft). A mutex is a
|
||||
{\bf MUT}ual {\bf EX}clusive device, which implies at most one thread
|
||||
can \jargon{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:
|
||||
|
||||
\begin{code}
|
||||
:- initialization
|
||||
mutex_create(addressbook).
|
||||
|
||||
change_address(Id, Address) :-
|
||||
mutex_lock(addressbook),
|
||||
retractall(address(Id, _)),
|
||||
asserta(address(Id, Address)),
|
||||
mutex_unlock(addressbook).
|
||||
\end{code}
|
||||
|
||||
|
||||
@table @code
|
||||
\predicate{mutex_create}{1}{?MutexId}
|
||||
Create a mutex. if @var{MutexId} is an atom, a \jargon{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.
|
||||
|
||||
\predicate{mutex_destroy}{1}{+MutexId}
|
||||
Destroy a mutex. After this call, @var{MutexId} becomes invalid and
|
||||
further references yield an \except{existence_error} exception.
|
||||
|
||||
\predicate{with_mutex}{2}{+MutexId, :Goal}
|
||||
Execute @var{Goal} while holding @var{MutexId}. If @var{Goal} leaves
|
||||
choicepointes, these are destroyed (as in once/1). The mutex is unlocked
|
||||
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
|
||||
successfully unlocked. See also mutex_create/2 and call_cleanup/3.
|
||||
|
||||
Although described in the thread-section, this predicate is also
|
||||
available in the single-threaded version, where it behaves simply as
|
||||
once/1.
|
||||
|
||||
\predicate{mutex_lock}{1}{+MutexId}
|
||||
Lock the mutex. Prolog mutexes are \jargon{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 @var{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. The predicate call_cleanup/[2-3] is another way to guarantee
|
||||
that the mutex is unlocked while retaining non-determinism.
|
||||
|
||||
\predicate{mutex_trylock}{1}{+MutexId}
|
||||
As mutex_lock/1, but if the mutex is held by another thread, this
|
||||
predicates fails immediately.
|
||||
|
||||
\predicate{mutex_unlock}{1}{+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 \except{permission_error}
|
||||
exception is raised.
|
||||
|
||||
\predicate{mutex_unlock_all}{0}{}
|
||||
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.
|
||||
|
||||
\predicate{current_mutex}{3}{?MutexId, ?ThreadId, ?Count}
|
||||
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{Count} with the recursive count of the mutex. Otherwise,
|
||||
@var{ThreadId} is @code{[]} and @var{Count} is 0.
|
||||
@end table
|
||||
|
||||
|
||||
|
||||
@node Parallelism, Tabling, Logtalk, Extensions
|
||||
@chapter Parallelism
|
||||
|
Reference in New Issue
Block a user