X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=doc%2Fmanual%2Fthreading.texinfo;h=f8f77da870eff731b39be98a69191b571e66f4a7;hb=refs%2Fheads%2Fcpuid;hp=6efed89ee8ec3ea711b93d332ebde5fe5ec80e31;hpb=a145b87809a5f266cc96e6a6e0e97b4a225bed2f;p=sbcl.git diff --git a/doc/manual/threading.texinfo b/doc/manual/threading.texinfo index 6efed89..f8f77da 100644 --- a/doc/manual/threading.texinfo +++ b/doc/manual/threading.texinfo @@ -5,38 +5,131 @@ SBCL supports a fairly low-level threading interface that maps onto the host operating system's concept of threads or lightweight processes. This means that threads may take advantage of hardware -multiprocessing on machines that have more than one CPU, but it does +multiprocessing on machines that have more than one CPU, but it does not allow Lisp control of the scheduler. This is found in the SB-THREAD package. -This requires x86 and Linux kernel 2.6 or systems with NPTL backports. +Threads are part of the default build on x86[-64] Linux only. + +They are also experimentally supported on: x86[-64] Darwin (Mac OS X), +x86[-64] FreeBSD, x86 SunOS (Solaris), and PPC Linux. On these platforms +threads must be explicitly enabled at build-time, see @file{INSTALL} for +directions. @menu +* Threading basics:: * Special Variables:: +* Atomic Operations:: * Mutex Support:: +* Semaphores:: * Waitqueue/condition variables:: +* Barriers:: * Sessions/Debugging:: -* Implementation (Linux x86):: +* Foreign threads:: +* Implementation (Linux x86/x86-64):: @end menu +@node Threading basics +@comment node-name, next, previous, up +@section Threading basics + +@lisp +(make-thread (lambda () (write-line "Hello, world"))) +@end lisp + +@subsection Thread Objects + +@include struct-sb-thread-thread.texinfo +@include var-sb-thread-star-current-thread-star.texinfo +@include fun-sb-thread-list-all-threads.texinfo +@include fun-sb-thread-thread-alive-p.texinfo +@include fun-sb-thread-thread-name.texinfo +@include fun-sb-thread-main-thread-p.texinfo +@include fun-sb-thread-main-thread.texinfo + +@subsection Making, Returning From, Joining, and Yielding Threads + +@include fun-sb-thread-make-thread.texinfo +@include macro-sb-thread-return-from-thread.texinfo +@include fun-sb-thread-abort-thread.texinfo +@include fun-sb-thread-join-thread.texinfo +@include fun-sb-thread-thread-yield.texinfo + +@subsection Asynchronous Operations + +@include fun-sb-thread-interrupt-thread.texinfo +@include fun-sb-thread-terminate-thread.texinfo + +@subsection Miscellaneous Operations + +@include fun-sb-thread-symbol-value-in-thread.texinfo + +@subsection Error Conditions + +@include condition-sb-thread-thread-error.texinfo +@include fun-sb-thread-thread-error-thread.texinfo + +@c @include condition-sb-thread-symbol-value-in-thread-error.texinfo +@include condition-sb-thread-interrupt-thread-error.texinfo +@include condition-sb-thread-join-thread-error.texinfo + @node Special Variables @comment node-name, next, previous, up @section Special Variables The interaction of special variables with multiple threads is mostly -as one would expect, but users of other Lisps are warned that the -behaviour of locally bound specials differs in places from what they -may expect. +as one would expect, with behaviour very similar to other +implementations. @itemize -@item +@item global special values are visible across all threads; @item bindings (e.g. using LET) are local to the thread; @item -initial values in a new thread are taken from the thread that created it. +threads do not inherit dynamic bindings from the parent thread @end itemize +The last point means that + +@lisp +(defparameter *x* 0) +(let ((*x* 1)) + (sb-thread:make-thread (lambda () (print *x*)))) +@end lisp + +prints @code{0} and not @code{1} as of 0.9.6. + +@node Atomic Operations +@comment node-name, next, previous, up +@section Atomic Operations + +Following atomic operations are particularly useful for implementing +lockless algorithms. + +@include macro-sb-ext-atomic-decf.texinfo +@include macro-sb-ext-atomic-incf.texinfo +@include macro-sb-ext-atomic-pop.texinfo +@include macro-sb-ext-atomic-push.texinfo +@include macro-sb-ext-atomic-update.texinfo +@include macro-sb-ext-compare-and-swap.texinfo + +@unnumberedsubsec CAS Protocol + +Our @code{compare-and-swap} is user-extensible using a protocol +similar to @code{setf}, allowing users to add CAS support to new +places via e.g. @code{defcas}. + +At the same time, new atomic operations can be built on top of CAS +using @code{get-cas-expansion}. See @code{atomic-update}, +@code{atomic-push}, and €@code{atomic-pop} for example of how to do +this. + +@include macro-sb-ext-cas.texinfo +@include macro-sb-ext-define-cas-expander.texinfo +@include macro-sb-ext-defcas.texinfo +@include fun-sb-ext-get-cas-expansion.texinfo + @node Mutex Support @comment node-name, next, previous, up @section Mutex Support @@ -46,10 +139,6 @@ thread is allowed to hold the mutex, others which attempt to take it will be made to wait until it's free. Threads are woken in the order that they go to sleep. -There isn't a timeout on mutex acquisition, but the usual WITH-TIMEOUT -macro (which throws a TIMEOUT condition after n seconds) can be used -if you want a bounded wait. - @lisp (defpackage :demo (:use "CL" "SB-THREAD" "SB-EXT")) @@ -58,18 +147,49 @@ if you want a bounded wait. (defvar *a-mutex* (make-mutex :name "my lock")) (defun thread-fn () - (let ((id (current-thread-id))) - (format t "Thread ~A running ~%" id) - (with-mutex (*a-mutex*) - (format t "Thread ~A got the lock~%" id) - (sleep (random 5))) - (format t "Thread ~A dropped lock, dying now~%" id))) + (format t "Thread ~A running ~%" *current-thread*) + (with-mutex (*a-mutex*) + (format t "Thread ~A got the lock~%" *current-thread*) + (sleep (random 5))) + (format t "Thread ~A dropped lock, dying now~%" *current-thread*)) (make-thread #'thread-fn) (make-thread #'thread-fn) - @end lisp +@include struct-sb-thread-mutex.texinfo + +@include macro-sb-thread-with-mutex.texinfo +@include macro-sb-thread-with-recursive-lock.texinfo + +@include fun-sb-thread-make-mutex.texinfo +@include fun-sb-thread-mutex-name.texinfo +@include fun-sb-thread-mutex-owner.texinfo +@include fun-sb-thread-mutex-value.texinfo +@include fun-sb-thread-grab-mutex.texinfo +@include fun-sb-thread-release-mutex.texinfo + +@node Semaphores +@comment node-name, next, previous, up +@section Semaphores + +Semaphores are among other things useful for keeping track of a +countable resource, e.g. messages in a queue, and sleep when the +resource is exhausted. + +@include struct-sb-thread-semaphore.texinfo +@include fun-sb-thread-make-semaphore.texinfo +@include fun-sb-thread-signal-semaphore.texinfo +@include fun-sb-thread-wait-on-semaphore.texinfo +@include fun-sb-thread-try-semaphore.texinfo +@include fun-sb-thread-semaphore-count.texinfo +@include fun-sb-thread-semaphore-name.texinfo + +@include struct-sb-thread-semaphore-notification.texinfo +@include fun-sb-thread-make-semaphore-notification.texinfo +@include fun-sb-thread-semaphore-notification-status.texinfo +@include fun-sb-thread-clear-semaphore-notification.texinfo + @node Waitqueue/condition variables @comment node-name, next, previous, up @section Waitqueue/condition variables @@ -85,29 +205,29 @@ when you weren't expecting it to. There are three components: @itemize -@item +@item the condition itself (not represented in code) -@item -the condition variable (a.k.a waitqueue) which proxies for it +@item +the condition variable (a.k.a. waitqueue) which proxies for it -@item -a lock to hold while testing the condition +@item +a lock to hold while testing the condition @end itemize Important stuff to be aware of: @itemize -@item +@item when calling condition-wait, you must hold the mutex. condition-wait will drop the mutex while it waits, and obtain it again before returning for whatever reason; -@item +@item likewise, you must be holding the mutex around calls to condition-notify; -@item +@item a process may return from condition-wait in several circumstances: it is not guaranteed that the underlying condition has become true. You must check that the resource is ready for whatever you want to do to @@ -129,25 +249,62 @@ it. (unless *buffer* (return)) (let ((head (car *buffer*))) (setf *buffer* (cdr *buffer*)) - (format t "reader ~A woke, read ~A~%" - (current-thread-id) head)))))) + (format t "reader ~A woke, read ~A~%" + *current-thread* head)))))) (defun writer () (loop (sleep (random 5)) (with-mutex (*buffer-lock*) (let ((el (intern - (string (code-char + (string (code-char (+ (char-code #\A) (random 26))))))) (setf *buffer* (cons el *buffer*))) (condition-notify *buffer-queue*)))) (make-thread #'writer) (make-thread #'reader) -(make-thread #'reader) - +(make-thread #'reader) @end lisp +@include struct-sb-thread-waitqueue.texinfo +@include fun-sb-thread-make-waitqueue.texinfo +@include fun-sb-thread-waitqueue-name.texinfo +@include fun-sb-thread-condition-wait.texinfo +@include fun-sb-thread-condition-notify.texinfo +@include fun-sb-thread-condition-broadcast.texinfo + +@node Barriers +@comment node-name, next, previous, up +@section Barriers + +These are based on the Linux kernel barrier design, which is in turn +based on the Alpha CPU memory model. They are presently implemented for +x86, x86-64, and PPC systems, and behave as compiler barriers on all +other CPUs. + +In addition to explicit use of the @code{sb-thread:barrier} macro, the +following functions and macros also serve as @code{:memory} barriers: + +@itemize +@item +@code{sb-ext:atomic-decf}, @code{sb-ext:atomic-incf}, @code{sb-ext:atomic-push}, +and @code{sb-ext:atomic-pop}. +@item +@code{sb-ext:compare-and-swap}. +@item +@code{sb-thread:grab-mutex}, @code{sb-thread:release-mutex}, +@code{sb-thread:with-mutex} and @code{sb-thread:with-recursive-lock}. +@item +@code{sb-thread:signal-semaphore}, @code{sb-thread:try-semaphore} and +@code{sb-thread:wait-on-semaphore}. +@item +@code{sb-thread:condition-wait}, @code{sb-thread:condition-notify} and +@code{sb-thread:condition-broadcast}. +@end itemize + +@include macro-sb-thread-barrier.texinfo + @node Sessions/Debugging @comment node-name, next, previous, up @section Sessions/Debugging @@ -159,7 +316,7 @@ view has its own collection of foreground/background/stopped threads. A thread which wishes to create a new session can use @code{sb-thread:with-new-session} to remove itself from the current session (which it shares with its parent and siblings) and create a -fresh one. +fresh one. # See also @code{sb-thread:make-listener-thread}. Within a single session, threads arbitrate between themselves for the @@ -173,25 +330,40 @@ it back into the background before it resumes. Arbitration for the input stream is managed by calls to @code{sb-thread:get-foreground} (which may block) and @code{sb-thread:release-foreground}. -@code{sb-ext:quit} terminates all threads in the current session, but -leaves other sessions running. - -@node Implementation (Linux x86) +@node Foreign threads +@comment node-name, next, previous, up +@section Foreign threads + +Direct calls to @code{pthread_create} (instead of @code{MAKE-THREAD}) +create threads that SBCL is not aware of, these are called foreign +threads. Currently, it is not possible to run Lisp code in such +threads. This means that the Lisp side signal handlers cannot work. +The best solution is to start foreign threads with signals blocked, +but since third party libraries may create threads, it is not always +feasible to do so. As a workaround, upon receiving a signal in a +foreign thread, SBCL changes the thread's sigmask to block all signals +that it wants to handle and resends the signal to the current process +which should land in a thread that does not block it, that is, a Lisp +thread. + +The resignalling trick cannot work for synchronously triggered signals +(SIGSEGV and co), take care not to trigger any. Resignalling for +synchronously triggered signals in foreign threads is subject to +@code{--lose-on-corruption}, see @ref{Runtime Options}. + +@node Implementation (Linux x86/x86-64) @comment node-name, next, previous, up -@section Implementation (Linux x86) - -On Linux x86, threading is implemented using @code{clone()} and does -not involve pthreads. This is not because there is anything wrong -with pthreads @emph{per se}, but there is plenty wrong (from our -perspective) with LinuxThreads. SBCL threads are mapped 1:1 onto -Linux tasks which share a VM but nothing else - each has its own -process id and can be seen in e.g. @command{ps} output. - -Per-thread local bindings for special variables is achieved using the -%fs segment register to point to a per-thread storage area. This may -cause interesting results if you link to foreign code that expects -threading or creates new threads, and the thread library in question -uses %fs in an incompatible way. +@section Implementation (Linux x86/x86-64) + +Threading is implemented using pthreads and some Linux specific bits +like futexes. + +On x86 the per-thread local bindings for special variables is achieved +using the %fs segment register to point to a per-thread storage area. +This may cause interesting results if you link to foreign code that +expects threading or creates new threads, and the thread library in +question uses %fs in an incompatible way. On x86-64 the r12 register +has a similar role. Queues require the @code{sys_futex()} system call to be available: this is the reason for the NPTL requirement. We test at runtime that