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:
vsc 2004-03-05 17:27:53 +00:00
parent caa0b26665
commit ad685b6e5b
5 changed files with 544 additions and 12 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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