Whitespace changes and add modeline that inhibits indentation with tabs.
[bordeaux-threads.git] / src / default-implementations.lisp
blob05a0782d989012e267f3fad2766638caabe79eec
1 ;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
3 (in-package #:bordeaux-threads)
5 ;;; Helper macros
7 (defmacro defdfun (name args doc &body body)
8 (let ((docstring (gensym "DOCSTRING")))
9 `(progn
10 ,(unless (fboundp name)
11 `(defun ,name ,args ,@body))
12 (let ((,docstring (documentation ',name 'function)))
13 (setf (documentation ',name 'function)
14 (if ,docstring
15 (format nil "~a~@[~%~%~a~]" ,doc ,docstring)
16 ,doc))))))
18 (defmacro defdmacro (name args doc &body body)
19 (let ((docstring (gensym "DOCSTRING")))
20 `(progn
21 ,(unless (fboundp name)
22 `(defmacro ,name ,args ,@body))
23 (let ((,docstring (documentation ',name 'function)))
24 (setf (documentation ',name 'function)
25 (if ,docstring
26 (format nil "~a~@[~%~%~a~]" ,doc ,docstring)
27 ,doc))))))
29 ;;; Thread Creation
31 (defdfun make-thread (function &key name)
32 "Creates and returns a thread named NAME, which will call the
33 function FUNCTION with no arguments: when FUNCTION returns, the
34 thread terminates. NAME defaults to NIL if unsupplied.
36 On systems that do not support multi-threading, MAKE-THREAD will
37 signal an error.
39 The interaction between threads and dynamic variables is in some
40 cases complex, and depends on whether the variable has only a global
41 binding (as established by e.g. DEFVAR/DEFPARAMETER/top-level SETQ)
42 or has been bound locally (e.g. with LET or LET*) in the calling
43 thread.
45 - Global bindings are shared between threads: the initial value of a
46 global variable in the new thread will be the same as in the
47 parent, and an assignment to such a variable in any thread will be
48 visible to all threads in which the global binding is visible.
50 - Local bindings are local to the thread they are introduced in,
51 except that
53 - Local bindings in the the caller of MAKE-THREAD may or may not be
54 shared with the new thread that it creates: this is
55 implementation-defined. Portable code should not depend on
56 particular behaviour in this case, nor should it assign to such
57 variables without first rebinding them in the new thread."
58 (declare (ignore function name))
59 (error (make-threading-support-error)))
61 (defdfun current-thread ()
62 "Returns the thread object for the calling
63 thread. This is the same kind of object as would be returned by
64 MAKE-THREAD."
65 nil)
67 (defdfun threadp (object)
68 "Returns true if object is a thread, otherwise NIL."
69 (declare (ignore object))
70 nil)
72 (defdfun thread-name (thread)
73 "Returns the name of the thread, as supplied to MAKE-THREAD."
74 (declare (ignore thread))
75 "Main thread")
77 ;;; Resource contention: locks and recursive locks
79 (defdfun make-lock (&optional name)
80 "Creates a lock (a mutex) whose name is NAME. If the system does not
81 support multiple threads this will still return some object, but it
82 may not be used for very much."
83 ;; In CLIM-SYS this is a freshly consed list (NIL). I don't know if
84 ;; there's some good reason it should be said structure or that it
85 ;; be freshly consed - EQ comparison of locks?
86 (declare (ignore name))
87 (list nil))
89 (defdfun acquire-lock (lock &optional wait-p)
90 "Acquire the lock LOCK for the calling thread.
91 WAIT-P governs what happens if the lock is not available: if WAIT-P
92 is true, the calling thread will wait until the lock is available
93 and then acquire it; if WAIT-P is NIL, ACQUIRE-LOCK will return
94 immediately. ACQUIRE-LOCK returns true if the lock was acquired and
95 NIL otherwise.
97 This specification does not define what happens if a thread
98 attempts to acquire a lock that it already holds. For applications
99 that require locks to be safe when acquired recursively, see instead
100 MAKE-RECURSIVE-LOCK and friends."
101 (declare (ignore lock wait-p))
104 (defdfun release-lock (lock)
105 "Release LOCK. It is an error to call this unless
106 the lock has previously been acquired (and not released) by the same
107 thread. If other threads are waiting for the lock, the
108 ACQUIRE-LOCK call in one of them will now be able to continue.
110 This function has no interesting return value."
111 (declare (ignore lock))
112 (values))
114 (defdmacro with-lock-held ((place) &body body)
115 "Evaluates BODY with the lock named by PLACE, the value of which
116 is a lock created by MAKE-LOCK. Before the forms in BODY are
117 evaluated, the lock is acquired as if by using ACQUIRE-LOCK. After the
118 forms in BODY have been evaluated, or if a non-local control transfer
119 is caused (e.g. by THROW or SIGNAL), the lock is released as if by
120 RELEASE-LOCK.
122 Note that if the debugger is entered, it is unspecified whether the
123 lock is released at debugger entry or at debugger exit when execution
124 is restarted."
125 `(when (acquire-lock ,place t)
126 (unwind-protect
127 (locally ,@body)
128 (release-lock ,place))))
130 (defdfun make-recursive-lock (&optional name)
131 "Create and return a recursive lock whose name is NAME. A recursive
132 lock differs from an ordinary lock in that a thread that already
133 holds the recursive lock can acquire it again without blocking. The
134 thread must then release the lock twice before it becomes available
135 for another thread."
136 (declare (ignore name))
137 (list nil))
139 (defdfun acquire-recursive-lock (lock)
140 "As for ACQUIRE-LOCK, but for recursive locks."
141 (declare (ignore lock))
144 (defdfun release-recursive-lock (lock)
145 "Release the recursive LOCK. The lock will only
146 become free after as many Release operations as there have been
147 Acquire operations. See RELEASE-LOCK for other information."
148 (declare (ignore lock))
149 (values))
151 (defdmacro with-recursive-lock-held ((place &key timeout) &body body)
152 "Evaluates BODY with the recursive lock named by PLACE, which is a
153 reference to a recursive lock created by MAKE-RECURSIVE-LOCK. See
154 WITH-LOCK-HELD etc etc"
155 (declare (ignore timeout))
156 `(when (acquire-recursive-lock ,place)
157 (unwind-protect
158 (locally ,@body)
159 (release-recursive-lock ,place))))
161 ;;; Resource contention: condition variables
163 ;;; A condition variable provides a mechanism for threads to put
164 ;;; themselves to sleep while waiting for the state of something to
165 ;;; change, then to be subsequently woken by another thread which has
166 ;;; changed the state.
168 ;;; A condition variable must be used in conjunction with a lock to
169 ;;; protect access to the state of the object of interest. The
170 ;;; procedure is as follows:
172 ;;; Suppose two threads A and B, and some kind of notional event
173 ;;; channel C. A is consuming events in C, and B is producing them.
174 ;;; CV is a condition-variable
176 ;;; 1) A acquires the lock that safeguards access to C
177 ;;; 2) A threads and removes all events that are available in C
178 ;;; 3) When C is empty, A calls CONDITION-WAIT, which atomically
179 ;;; releases the lock and puts A to sleep on CV
180 ;;; 4) Wait to be notified; CONDITION-WAIT will acquire the lock again
181 ;;; before returning
182 ;;; 5) Loop back to step 2, for as long as threading should continue
184 ;;; When B generates an event E, it
185 ;;; 1) acquires the lock guarding C
186 ;;; 2) adds E to the channel
187 ;;; 3) calls CONDITION-NOTIFY on CV to wake any sleeping thread
188 ;;; 4) releases the lock
190 ;;; To avoid the "lost wakeup" problem, the implementation must
191 ;;; guarantee that CONDITION-WAIT in thread A atomically releases the
192 ;;; lock and sleeps. If this is not guaranteed there is the
193 ;;; possibility that thread B can add an event and call
194 ;;; CONDITION-NOTIFY between the lock release and the sleep - in this
195 ;;; case the notify call would not see A, which would be left sleeping
196 ;;; despite there being an event available.
198 (defdfun thread-yield ()
199 "Allows other threads to run. It may be necessary or desirable to
200 call this periodically in some implementations; others may schedule
201 threads automatically. On systems that do not support
202 multi-threading, this does nothing."
203 (values))
205 (defdfun make-condition-variable ()
206 "Returns a new condition-variable object for use
207 with CONDITION-WAIT and CONDITION-NOTIFY."
208 nil)
210 (defdfun condition-wait (condition-variable lock)
211 "Atomically release LOCK and enqueue the calling
212 thread waiting for CONDITION-VARIABLE. The thread will resume when
213 another thread has notified it using CONDITION-NOTIFY; it may also
214 resume if interrupted by some external event or in other
215 implementation-dependent circumstances: the caller must always test
216 on waking that there is threading to be done, instead of assuming
217 that it can go ahead.
219 However and for whatever reason the thread is resumed, the system
220 always reacquires LOCK before returning to the caller. It is an
221 error to call this unless from the thread that holds LOCK.
223 In an implementation that does not support multiple threads, this
224 function signals an error."
225 (declare (ignore condition-variable lock))
226 (error (make-threading-support-error)))
228 (defdfun condition-notify (condition-variable)
229 "Notify at least one of the threads waiting for
230 CONDITION-VARIABLE. It is implementation-dependent whether one or
231 more than one (and possibly all) threads are woken, but if the
232 implementation is capable of waking only a single thread (not all
233 are) this is probably preferable for efficiency reasons. The order
234 of wakeup is unspecified and does not necessarily relate to the
235 order that the threads went to sleep in.
237 CONDITION-NOTIFY has no useful return value. In an implementation
238 that does not support multiple threads, it has no effect."
239 (declare (ignore condition-variable))
240 (values))
242 ;;; Timeouts
244 (defmacro with-timeout ((timeout) &body body)
245 "Execute `BODY' and signal a condition of type TIMEOUT if the execution of
246 BODY does not complete within `TIMEOUT' seconds. On implementations which do not
247 support WITH-TIMEOUT natively and don't support threads either it has no effect."
248 #+thread-support
249 (let ((ok-tag (gensym "OK"))
250 (timeout-tag (gensym "TIMEOUT"))
251 (caller (gensym "CALLER"))
252 (sleeper (gensym "SLEEPER")))
253 `(let (,sleeper)
254 (multiple-value-prog1
255 (catch ',ok-tag
256 (catch ',timeout-tag
257 (let ((,caller (current-thread)))
258 (setf ,sleeper
259 (make-thread #'(lambda ()
260 (sleep ,timeout)
261 (interrupt-thread ,caller
262 #'(lambda ()
263 (ignore-errors
264 (throw ',timeout-tag nil)))))
265 :name (format nil "WITH-TIMEOUT thread serving: ~S."
266 (thread-name ,caller))))
267 (throw ',ok-tag (progn ,@body))))
268 (error 'timeout))
269 (when (thread-alive-p ,sleeper)
270 (destroy-thread ,sleeper)))))
271 #-thread-support
272 `(progn
273 ,@body))
275 ;;; Introspection/debugging
277 ;;; The following functions may be provided for debugging purposes,
278 ;;; but are not advised to be called from normal user code.
280 (defdfun all-threads ()
281 "Returns a sequence of all of the threads. This may not
282 be freshly-allocated, so the caller should not modify it."
283 (error (make-threading-support-error)))
285 (defdfun interrupt-thread (thread function)
286 "Interrupt THREAD and cause it to evaluate FUNCTION
287 before continuing with the interrupted path of execution. This may
288 not be a good idea if THREAD is holding locks or doing anything
289 important. On systems that do not support multiple threads, this
290 function signals an error."
291 (declare (ignore thread function))
292 (error (make-threading-support-error)))
294 (defdfun destroy-thread (thread)
295 "Terminates the thread THREAD, which is an object
296 as returned by MAKE-THREAD. This should be used with caution: it is
297 implementation-defined whether the thread runs cleanup forms or
298 releases its locks first.
300 Destroying the calling thread is an error."
301 (declare (ignore thread))
302 (error (make-threading-support-error)))
304 (defdfun thread-alive-p (thread)
305 "Returns true if THREAD is alive, that is, if
306 DESTROY-THREAD has not been called on it."
307 (declare (ignore thread))
308 (error (make-threading-support-error)))
310 (defdfun thread-join (thread)
311 "Wait until THREAD terminates. If THREAD
312 has already terminated, return immediately."
313 (declare (ignore thread))
314 (error (make-threading-support-error)))