X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=doc%2Fmanual%2Fthreading.texinfo;h=b51ba34a902baa9477dd2e5a1e8d661680f06b4b;hb=d84e1dbbbf11e76663cfaa0b1a5b7591f39f01b6;hp=6efed89ee8ec3ea711b93d332ebde5fe5ec80e31;hpb=a145b87809a5f266cc96e6a6e0e97b4a225bed2f;p=sbcl.git diff --git a/doc/manual/threading.texinfo b/doc/manual/threading.texinfo index 6efed89..b51ba34 100644 --- a/doc/manual/threading.texinfo +++ b/doc/manual/threading.texinfo @@ -5,38 +5,108 @@ 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 + +@subsection Making, Joining, and Yielding Threads + +@include fun-sb-thread-make-thread.texinfo +@include fun-sb-thread-thread-yield.texinfo +@include fun-sb-thread-join-thread.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 + +SBCL provides a few special purpose atomic operations, particularly +useful for implementing lockless algorithms. + +@include macro-sb-ext-atomic-decf.texinfo +@include macro-sb-ext-atomic-incf.texinfo +@include macro-sb-ext-compare-and-swap.texinfo + @node Mutex Support @comment node-name, next, previous, up @section Mutex Support @@ -58,18 +128,40 @@ 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 fun-sb-thread-make-mutex.texinfo +@include fun-sb-thread-mutex-name.texinfo +@include fun-sb-thread-mutex-value.texinfo +@include fun-sb-thread-get-mutex.texinfo +@include fun-sb-thread-release-mutex.texinfo +@include macro-sb-thread-with-mutex.texinfo +@include macro-sb-thread-with-recursive-lock.texinfo + +@node Semaphores +@comment node-name, next, previous, up +@section Semaphores + +described here should be considered +experimental, subject to API changes without notice. + +@include struct-sb-thread-semaphore.texinfo +@include fun-sb-thread-make-semaphore.texinfo +@include fun-sb-thread-semaphore-count.texinfo +@include fun-sb-thread-semaphore-name.texinfo +@include fun-sb-thread-signal-semaphore.texinfo +@include fun-sb-thread-try-semaphore.texinfo +@include fun-sb-thread-wait-on-semaphore.texinfo + @node Waitqueue/condition variables @comment node-name, next, previous, up @section Waitqueue/condition variables @@ -85,29 +177,29 @@ when you weren't expecting it to. There are three components: @itemize -@item +@item the condition itself (not represented in code) -@item +@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 +221,61 @@ 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} and @code{sb-ext:atomic-incf}. +@item +@code{sb-ext:compare-and-swap}. +@item +@code{sb-thread:get-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 +287,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 @@ -176,22 +304,40 @@ input stream is managed by calls to @code{sb-thread:get-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 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 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/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