Remove compiler macros
[bordeaux-threads.git] / apiv1 / default-implementations.lisp
blob0c8e7f542ffb48e4860f9a13c822f535ce7fab3f
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: BORDEAUX-THREADS -*-
2 ;;;; The above modeline is required for Genera. Do not change.
4 (in-package #:bordeaux-threads)
6 ;;; Helper macros
8 (defmacro defdfun (name args doc &body body)
9 `(eval-when (:compile-toplevel :load-toplevel :execute)
10 (unless (fboundp ',name)
11 (defun ,name ,args ,@body))
12 (setf (documentation ',name 'function)
13 (or (documentation ',name 'function) ,doc))))
15 (defmacro defdmacro (name args doc &body body)
16 `(eval-when (:compile-toplevel :load-toplevel :execute)
17 (unless (fboundp ',name)
18 (defmacro ,name ,args ,@body))
19 (setf (documentation ',name 'function)
20 (or (documentation ',name 'function) ,doc))))
22 ;;; Thread Creation
24 (defdfun start-multiprocessing ()
25 "If the host implementation uses user-level threads, start the
26 scheduler and multiprocessing, otherwise do nothing.
27 It is safe to call repeatedly."
28 nil)
30 (defdfun make-thread (function &key name
31 (initial-bindings *default-special-bindings*))
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 \"Anonymous thread\" 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, such as the ones introduced by INITIAL-BINDINGS,
51 are local to the thread they are introduced in, 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 (warn "Bordeaux-Threads APIv1 is deprecated. Please migrate to APIv2.")
59 (%make-thread (binding-default-specials function initial-bindings)
60 (or name "Anonymous thread")))
62 (defdfun %make-thread (function name)
63 "The actual implementation-dependent function that creates threads."
64 (declare (ignore function name))
65 (error (make-threading-support-error)))
67 (defdfun current-thread ()
68 "Returns the thread object for the calling
69 thread. This is the same kind of object as would be returned by
70 MAKE-THREAD."
71 nil)
73 (defdfun threadp (object)
74 "Returns true if object is a thread, otherwise NIL."
75 (declare (ignore object))
76 nil)
78 (defdfun thread-name (thread)
79 "Returns the name of the thread, as supplied to MAKE-THREAD."
80 (declare (ignore thread))
81 "Main thread")
83 ;;; Resource contention: locks and recursive locks
85 (defdfun lock-p (object)
86 "Returns T if OBJECT is a lock; returns NIL otherwise."
87 (declare (ignore object))
88 nil)
90 (defdfun recursive-lock-p (object)
91 "Returns T if OBJECT is a recursive lock; returns NIL otherwise."
92 (declare (ignore object))
93 nil)
95 (defdfun make-lock (&optional name)
96 "Creates a lock (a mutex) whose name is NAME. If the system does not
97 support multiple threads this will still return some object, but it
98 may not be used for very much."
99 ;; In CLIM-SYS this is a freshly consed list (NIL). I don't know if
100 ;; there's some good reason it should be said structure or that it
101 ;; be freshly consed - EQ comparison of locks?
102 (declare (ignore name))
103 (list nil))
105 (defdfun acquire-lock (lock &optional wait-p)
106 "Acquire the lock LOCK for the calling thread.
107 WAIT-P governs what happens if the lock is not available: if WAIT-P
108 is true, the calling thread will wait until the lock is available
109 and then acquire it; if WAIT-P is NIL, ACQUIRE-LOCK will return
110 immediately. ACQUIRE-LOCK returns true if the lock was acquired and
111 NIL otherwise.
113 This specification does not define what happens if a thread
114 attempts to acquire a lock that it already holds. For applications
115 that require locks to be safe when acquired recursively, see instead
116 MAKE-RECURSIVE-LOCK and friends."
117 (declare (ignore lock wait-p))
120 (defdfun release-lock (lock)
121 "Release LOCK. It is an error to call this unless
122 the lock has previously been acquired (and not released) by the same
123 thread. If other threads are waiting for the lock, the
124 ACQUIRE-LOCK call in one of them will now be able to continue.
126 This function has no interesting return value."
127 (declare (ignore lock))
128 (values))
130 (defdmacro with-lock-held ((place) &body body)
131 "Evaluates BODY with the lock named by PLACE, the value of which
132 is a lock created by MAKE-LOCK. Before the forms in BODY are
133 evaluated, the lock is acquired as if by using ACQUIRE-LOCK. After the
134 forms in BODY have been evaluated, or if a non-local control transfer
135 is caused (e.g. by THROW or SIGNAL), the lock is released as if by
136 RELEASE-LOCK.
138 Note that if the debugger is entered, it is unspecified whether the
139 lock is released at debugger entry or at debugger exit when execution
140 is restarted."
141 `(when (acquire-lock ,place t)
142 (unwind-protect
143 (locally ,@body)
144 (release-lock ,place))))
146 (defdfun make-recursive-lock (&optional name)
147 "Create and return a recursive lock whose name is NAME. A recursive
148 lock differs from an ordinary lock in that a thread that already
149 holds the recursive lock can acquire it again without blocking. The
150 thread must then release the lock twice before it becomes available
151 for another thread."
152 (declare (ignore name))
153 (list nil))
155 (defdfun acquire-recursive-lock (lock)
156 "As for ACQUIRE-LOCK, but for recursive locks."
157 (declare (ignore lock))
160 (defdfun release-recursive-lock (lock)
161 "Release the recursive LOCK. The lock will only
162 become free after as many Release operations as there have been
163 Acquire operations. See RELEASE-LOCK for other information."
164 (declare (ignore lock))
165 (values))
167 (defdmacro with-recursive-lock-held ((place &key timeout) &body body)
168 "Evaluates BODY with the recursive lock named by PLACE, which is a
169 reference to a recursive lock created by MAKE-RECURSIVE-LOCK. See
170 WITH-LOCK-HELD etc etc"
171 (declare (ignore timeout))
172 `(when (acquire-recursive-lock ,place)
173 (unwind-protect
174 (locally ,@body)
175 (release-recursive-lock ,place))))
177 ;;; Resource contention: condition variables
179 ;;; A condition variable provides a mechanism for threads to put
180 ;;; themselves to sleep while waiting for the state of something to
181 ;;; change, then to be subsequently woken by another thread which has
182 ;;; changed the state.
184 ;;; A condition variable must be used in conjunction with a lock to
185 ;;; protect access to the state of the object of interest. The
186 ;;; procedure is as follows:
188 ;;; Suppose two threads A and B, and some kind of notional event
189 ;;; channel C. A is consuming events in C, and B is producing them.
190 ;;; CV is a condition-variable
192 ;;; 1) A acquires the lock that safeguards access to C
193 ;;; 2) A threads and removes all events that are available in C
194 ;;; 3) When C is empty, A calls CONDITION-WAIT, which atomically
195 ;;; releases the lock and puts A to sleep on CV
196 ;;; 4) Wait to be notified; CONDITION-WAIT will acquire the lock again
197 ;;; before returning
198 ;;; 5) Loop back to step 2, for as long as threading should continue
200 ;;; When B generates an event E, it
201 ;;; 1) acquires the lock guarding C
202 ;;; 2) adds E to the channel
203 ;;; 3) calls CONDITION-NOTIFY on CV to wake any sleeping thread
204 ;;; 4) releases the lock
206 ;;; To avoid the "lost wakeup" problem, the implementation must
207 ;;; guarantee that CONDITION-WAIT in thread A atomically releases the
208 ;;; lock and sleeps. If this is not guaranteed there is the
209 ;;; possibility that thread B can add an event and call
210 ;;; CONDITION-NOTIFY between the lock release and the sleep - in this
211 ;;; case the notify call would not see A, which would be left sleeping
212 ;;; despite there being an event available.
214 (defdfun thread-yield ()
215 "Allows other threads to run. It may be necessary or desirable to
216 call this periodically in some implementations; others may schedule
217 threads automatically. On systems that do not support
218 multi-threading, this does nothing."
219 (values))
221 (defdfun make-condition-variable (&key name)
222 "Returns a new condition-variable object for use
223 with CONDITION-WAIT and CONDITION-NOTIFY."
224 (declare (ignore name))
225 nil)
227 (defdfun condition-wait (condition-variable lock &key timeout)
228 "Atomically release LOCK and enqueue the calling
229 thread waiting for CONDITION-VARIABLE. The thread will resume when
230 another thread has notified it using CONDITION-NOTIFY; it may also
231 resume if interrupted by some external event or in other
232 implementation-dependent circumstances: the caller must always test
233 on waking that there is threading to be done, instead of assuming
234 that it can go ahead.
236 It is an error to call function this unless from the thread that
237 holds LOCK.
239 If TIMEOUT is nil or not provided, the call blocks until a
240 notification is received.
242 If TIMEOUT is non-nil, the call will return after at most TIMEOUT
243 seconds (approximately), whether or not a notification has occurred.
245 Either NIL or T will be returned. A return of NIL indicates that the
246 timeout has expired without receiving a notification. A return of T
247 indicates that a notification was received.
249 In an implementation that does not support multiple threads, this
250 function signals an error."
251 (declare (ignore condition-variable lock timeout))
252 (error (make-threading-support-error)))
254 (defdfun condition-notify (condition-variable)
255 "Notify at least one of the threads waiting for
256 CONDITION-VARIABLE. It is implementation-dependent whether one or
257 more than one (and possibly all) threads are woken, but if the
258 implementation is capable of waking only a single thread (not all
259 are) this is probably preferable for efficiency reasons. The order
260 of wakeup is unspecified and does not necessarily relate to the
261 order that the threads went to sleep in.
263 CONDITION-NOTIFY has no useful return value. In an implementation
264 that does not support multiple threads, it has no effect."
265 (declare (ignore condition-variable))
266 (values))
268 ;;; Resource contention: semaphores
270 (defdfun make-semaphore (&key name (count 0))
271 "Create a semaphore with the supplied NAME and initial counter value COUNT."
272 (make-%semaphore :lock (make-lock name)
273 :condition-variable (make-condition-variable :name name)
274 :counter count))
276 (defdfun signal-semaphore (semaphore &key (count 1))
277 "Increment SEMAPHORE by COUNT. If there are threads waiting on this
278 semaphore, then COUNT of them are woken up."
279 (with-lock-held ((%semaphore-lock semaphore))
280 (incf (%semaphore-counter semaphore) count)
281 (dotimes (v count)
282 (condition-notify (%semaphore-condition-variable semaphore))))
283 (values))
285 (defdfun wait-on-semaphore (semaphore &key timeout)
286 "Decrement the count of SEMAPHORE by 1 if the count would not be negative.
288 Else blocks until the semaphore can be decremented. Returns generalized boolean
289 T on success.
291 If TIMEOUT is given, it is the maximum number of seconds to wait. If the count
292 cannot be decremented in that time, returns NIL without decrementing the count."
293 (with-lock-held ((%semaphore-lock semaphore))
294 (if (>= (%semaphore-counter semaphore) 1)
295 (decf (%semaphore-counter semaphore))
296 (let ((deadline (when timeout
297 (+ (get-internal-real-time)
298 (* timeout internal-time-units-per-second)))))
299 ;; we need this loop because of a spurious wakeup possibility
300 (loop until (>= (%semaphore-counter semaphore) 1)
301 do (cond
302 ((null (condition-wait (%semaphore-condition-variable semaphore)
303 (%semaphore-lock semaphore)
304 :timeout timeout))
305 (return-from wait-on-semaphore))
306 ;; unfortunately cv-wait may return T on timeout too
307 ((and deadline (>= (get-internal-real-time) deadline))
308 (return-from wait-on-semaphore))
309 (timeout
310 (setf timeout (/ (- deadline (get-internal-real-time))
311 internal-time-units-per-second)))))
312 (decf (%semaphore-counter semaphore))))))
314 (defdfun semaphore-p (object)
315 "Returns T if OBJECT is a semaphore; returns NIL otherwise."
316 (typep object 'semaphore))
318 ;;; Introspection/debugging
320 ;;; The following functions may be provided for debugging purposes,
321 ;;; but are not advised to be called from normal user code.
323 (defdfun all-threads ()
324 "Returns a sequence of all of the threads. This may not
325 be freshly-allocated, so the caller should not modify it."
326 (error (make-threading-support-error)))
328 (defdfun interrupt-thread (thread function)
329 "Interrupt THREAD and cause it to evaluate FUNCTION
330 before continuing with the interrupted path of execution. This may
331 not be a good idea if THREAD is holding locks or doing anything
332 important. On systems that do not support multiple threads, this
333 function signals an error."
334 (declare (ignore thread function))
335 (error (make-threading-support-error)))
337 (defdfun destroy-thread (thread)
338 "Terminates the thread THREAD, which is an object
339 as returned by MAKE-THREAD. This should be used with caution: it is
340 implementation-defined whether the thread runs cleanup forms or
341 releases its locks first.
343 Destroying the calling thread is an error."
344 (declare (ignore thread))
345 (error (make-threading-support-error)))
347 (defdfun thread-alive-p (thread)
348 "Returns true if THREAD is alive, that is, if
349 DESTROY-THREAD has not been called on it."
350 (declare (ignore thread))
351 (error (make-threading-support-error)))
353 (defdfun join-thread (thread)
354 "Wait until THREAD terminates. If THREAD has already terminated,
355 return immediately. The return values of the thread function are
356 returned."
357 (declare (ignore thread))
358 (error (make-threading-support-error)))