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
|
#endif
|
||||||
|
|
||||||
|
#if LOW_PROF
|
||||||
|
#include <stdio.h>
|
||||||
|
void prof_alrm(int signo)
|
||||||
|
{
|
||||||
|
fprintf(FProf,"%p\n", PREG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
Int
|
Int
|
||||||
Yap_absmi(int inp)
|
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
|
|
||||||
|
@ -630,7 +630,7 @@ init_dbtable(tr_fr_ptr trail_ptr) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DynamicClause *dcl = ClauseFlagsToDynamicClause(pt0);
|
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->ClRefCount = 0;
|
||||||
ncl->u.ParentIndex = blk->u.ParentIndex;
|
ncl->u.ParentIndex = blk->u.ParentIndex;
|
||||||
ncl->ChildIndex = NULL;
|
ncl->ChildIndex = NULL;
|
||||||
|
ncl->ClSize = sz;
|
||||||
INIT_LOCK(ncl->ClLock);
|
INIT_LOCK(ncl->ClLock);
|
||||||
INIT_CLREF_COUNT(ncl);
|
INIT_CLREF_COUNT(ncl);
|
||||||
codep = start = ncl->ClCode;
|
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)@*
|
Copyright @copyright{} 1996-98 LMU (Ludwig-Maximilians-University)@*
|
||||||
Munich, Germany
|
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
|
@menu
|
||||||
* Intro:: Introduction
|
* Intro:: Introduction
|
||||||
* Install:: Installation
|
* Install:: Installation
|
||||||
@ -129,6 +134,7 @@ Munich, Germany
|
|||||||
* CLPQR:: The CLP(Q,R) System
|
* CLPQR:: The CLP(Q,R) System
|
||||||
* CHR:: The CHR System
|
* CHR:: The CHR System
|
||||||
* Logtalk:: The Logtalk Object-Oriented System
|
* Logtalk:: The Logtalk Object-Oriented System
|
||||||
|
* Threads:: Thread Library
|
||||||
* Parallelism:: Running in Or-Parallel
|
* Parallelism:: Running in Or-Parallel
|
||||||
* Tabling:: Storing Intermediate Solutions of programs
|
* Tabling:: Storing Intermediate Solutions of programs
|
||||||
* Low Level Profiling:: Profiling Abstract Machine Instructions
|
* Low Level Profiling:: Profiling Abstract Machine Instructions
|
||||||
@ -8738,6 +8744,7 @@ Extensions to Traditional Prolog
|
|||||||
* Attributed Variables:: Using attributed Variables
|
* Attributed Variables:: Using attributed Variables
|
||||||
* CLPQR:: The CLP(Q,R) System
|
* CLPQR:: The CLP(Q,R) System
|
||||||
* Logtalk:: The Logtalk Object-Oriented system
|
* Logtalk:: The Logtalk Object-Oriented system
|
||||||
|
* Threads:: Thread Library
|
||||||
* Parallelism:: Running in Or-Parallel
|
* Parallelism:: Running in Or-Parallel
|
||||||
* Tabling:: Storing Intermediate Solutions of programs
|
* Tabling:: Storing Intermediate Solutions of programs
|
||||||
* Low Level Profiling:: Profiling Abstract Machine Instructions
|
* Low Level Profiling:: Profiling Abstract Machine Instructions
|
||||||
@ -12103,7 +12110,7 @@ debugging environment is not available in SICStus Prolog.
|
|||||||
@end enumerate
|
@end enumerate
|
||||||
|
|
||||||
|
|
||||||
@node Logtalk, Parallelism, CHR, Extensions
|
@node Logtalk, Threads, CHR, Extensions
|
||||||
@chapter Logtalk
|
@chapter Logtalk
|
||||||
@cindex logtalk
|
@cindex logtalk
|
||||||
|
|
||||||
@ -12116,6 +12123,530 @@ modules in its implementation.
|
|||||||
Logtalk documentation is included in the Logtalk directory. For the
|
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
|
||||||
|
@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
|
@node Parallelism, Tabling, Logtalk, Extensions
|
||||||
@chapter Parallelism
|
@chapter Parallelism
|
||||||
|
Reference in New Issue
Block a user