2 @comment node-name, next, previous, up
5 SBCL supports a fairly low-level threading interface that maps onto
6 the host operating system's concept of threads or lightweight
7 processes. This means that threads may take advantage of hardware
8 multiprocessing on machines that have more than one CPU, but it does
9 not allow Lisp control of the scheduler. This is found in the
12 This requires x86 and Linux kernel 2.6 or systems with NPTL backports.
17 * Waitqueue/condition variables::
18 * Sessions/Debugging::
19 * Implementation (Linux x86)::
22 @node Special Variables
23 @comment node-name, next, previous, up
24 @section Special Variables
26 The interaction of special variables with multiple threads is mostly
27 as one would expect, but users of other Lisps are warned that the
28 behaviour of locally bound specials differs in places from what they
33 global special values are visible across all threads;
35 bindings (e.g. using LET) are local to the thread;
37 initial values in a new thread are taken from the thread that created it.
41 @comment node-name, next, previous, up
42 @section Mutex Support
44 Mutexes are used for controlling access to a shared resource. One
45 thread is allowed to hold the mutex, others which attempt to take it
46 will be made to wait until it's free. Threads are woken in the order
47 that they go to sleep.
49 There isn't a timeout on mutex acquisition, but the usual WITH-TIMEOUT
50 macro (which throws a TIMEOUT condition after n seconds) can be used
51 if you want a bounded wait.
54 (defpackage :demo (:use "CL" "SB-THREAD" "SB-EXT"))
58 (defvar *a-mutex* (make-mutex :name "my lock"))
61 (let ((id (current-thread-id)))
62 (format t "Thread ~A running ~%" id)
63 (with-mutex (*a-mutex*)
64 (format t "Thread ~A got the lock~%" id)
66 (format t "Thread ~A dropped lock, dying now~%" id)))
68 (make-thread #'thread-fn)
69 (make-thread #'thread-fn)
73 @node Waitqueue/condition variables
74 @comment node-name, next, previous, up
75 @section Waitqueue/condition variables
77 These are based on the POSIX condition variable design, hence the
78 annoyingly CL-conflicting name. For use when you want to check a
79 condition and sleep until it's true. For example: you have a shared
80 queue, a writer process checking ``queue is empty'' and one or more
81 readers that need to know when ``queue is not empty''. It sounds
82 simple, but is astonishingly easy to deadlock if another process runs
83 when you weren't expecting it to.
85 There are three components:
89 the condition itself (not represented in code)
92 the condition variable (a.k.a waitqueue) which proxies for it
95 a lock to hold while testing the condition
98 Important stuff to be aware of:
102 when calling condition-wait, you must hold the mutex. condition-wait
103 will drop the mutex while it waits, and obtain it again before
104 returning for whatever reason;
107 likewise, you must be holding the mutex around calls to
111 a process may return from condition-wait in several circumstances: it
112 is not guaranteed that the underlying condition has become true. You
113 must check that the resource is ready for whatever you want to do to
119 (defvar *buffer-queue* (make-waitqueue))
120 (defvar *buffer-lock* (make-mutex :name "buffer lock"))
122 (defvar *buffer* (list nil))
125 (with-mutex (*buffer-lock*)
127 (condition-wait *buffer-queue* *buffer-lock*)
129 (unless *buffer* (return))
130 (let ((head (car *buffer*)))
131 (setf *buffer* (cdr *buffer*))
132 (format t "reader ~A woke, read ~A~%"
133 (current-thread-id) head))))))
138 (with-mutex (*buffer-lock*)
141 (+ (char-code #\A) (random 26)))))))
142 (setf *buffer* (cons el *buffer*)))
143 (condition-notify *buffer-queue*))))
145 (make-thread #'writer)
146 (make-thread #'reader)
147 (make-thread #'reader)
151 @node Sessions/Debugging
152 @comment node-name, next, previous, up
153 @section Sessions/Debugging
155 If the user has multiple views onto the same Lisp image (for example,
156 using multiple terminals, or a windowing system, or network access)
157 they are typically set up as multiple @dfn{sessions} such that each
158 view has its own collection of foreground/background/stopped threads.
159 A thread which wishes to create a new session can use
160 @code{sb-thread:with-new-session} to remove itself from the current
161 session (which it shares with its parent and siblings) and create a
163 # See also @code{sb-thread:make-listener-thread}.
165 Within a single session, threads arbitrate between themselves for the
166 user's attention. A thread may be in one of three notional states:
167 foreground, background, or stopped. When a background process
168 attempts to print a repl prompt or to enter the debugger, it will stop
169 and print a message saying that it has stopped. The user at his
170 leisure may switch to that thread to find out what it needs. If a
171 background thread enters the debugger, selecting any restart will put
172 it back into the background before it resumes. Arbitration for the
173 input stream is managed by calls to @code{sb-thread:get-foreground}
174 (which may block) and @code{sb-thread:release-foreground}.
176 @code{sb-ext:quit} terminates all threads in the current session, but
177 leaves other sessions running.
179 @node Implementation (Linux x86)
180 @comment node-name, next, previous, up
181 @section Implementation (Linux x86)
183 On Linux x86, threading is implemented using @code{clone()} and does
184 not involve pthreads. This is not because there is anything wrong
185 with pthreads @emph{per se}, but there is plenty wrong (from our
186 perspective) with LinuxThreads. SBCL threads are mapped 1:1 onto
187 Linux tasks which share a VM but nothing else - each has its own
188 process id and can be seen in e.g. @command{ps} output.
190 Per-thread local bindings for special variables is achieved using the
191 %fs segment register to point to a per-thread storage area. This may
192 cause interesting results if you link to foreign code that expects
193 threading or creates new threads, and the thread library in question
194 uses %fs in an incompatible way.
196 Queues require the @code{sys_futex()} system call to be available:
197 this is the reason for the NPTL requirement. We test at runtime that
198 this system call exists.
200 Garbage collection is done with the existing Conservative Generational
201 GC. Allocation is done in small (typically 8k) regions: each thread
202 has its own region so this involves no stopping. However, when a
203 region fills, a lock must be obtained while another is allocated, and
204 when a collection is required, all processes are stopped. This is
205 achieved by sending them signals, which may make for interesting
206 behaviour if they are interrupted in system calls. The streams
207 interface is believed to handle the required system call restarting
208 correctly, but this may be a consideration when making other blocking
209 calls e.g. from foreign library code.
211 Large amounts of the SBCL library have not been inspected for
212 thread-safety. Some of the obviously unsafe areas have large locks
213 around them, so compilation and fasl loading, for example, cannot be
214 parallelized. Work is ongoing in this area.
216 A new thread by default is created in the same POSIX process group and
217 session as the thread it was created by. This has an impact on
218 keyboard interrupt handling: pressing your terminal's intr key
219 (typically @kbd{Control-C}) will interrupt all processes in the
220 foreground process group, including Lisp threads that SBCL considers
221 to be notionally `background'. This is undesirable, so background
222 threads are set to ignore the SIGINT signal.
224 @code{sb-thread:make-listener-thread} in addition to creating a new
225 Lisp session makes a new POSIX session, so that pressing
226 @kbd{Control-C} in one window will not interrupt another listener -
227 this has been found to be embarrassing.