From ad685b6e5baa5f8cd7485fe624039e629649ace3 Mon Sep 17 00:00:00 2001 From: vsc Date: Fri, 5 Mar 2004 17:27:53 +0000 Subject: [PATCH] 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 --- C/absmi.c | 18 +- C/cdmgr.c | 2 +- C/heapgc.c | 2 +- C/index.c | 1 + docs/yap.tex | 533 ++++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 544 insertions(+), 12 deletions(-) diff --git a/C/absmi.c b/C/absmi.c index c182686c6..69eff2e09 100644 --- a/C/absmi.c +++ b/C/absmi.c @@ -113,6 +113,15 @@ push_live_regs(yamop *pco) } #endif +#if LOW_PROF +#include +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 -void prof_alrm(int signo) -{ - fprintf(FProf,"%p\n", PREG); - return; -} - -#endif diff --git a/C/cdmgr.c b/C/cdmgr.c index e1010ab63..b951d2083 100644 --- a/C/cdmgr.c +++ b/C/cdmgr.c @@ -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); diff --git a/C/heapgc.c b/C/heapgc.c index 0ee44a04f..ea5dbbff1 100644 --- a/C/heapgc.c +++ b/C/heapgc.c @@ -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); } } } diff --git a/C/index.c b/C/index.c index d3ace8f68..70440b147 100644 --- a/C/index.c +++ b/C/index.c @@ -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; diff --git a/docs/yap.tex b/docs/yap.tex index b6f3dcc52..ca56b557f 100644 --- a/docs/yap.tex +++ b/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_get_message(a(A)), + + + 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