1 ;;;; various extensions (including SB-INT "internal extensions")
2 ;;;; available both in the cross-compilation host Lisp and in the
3 ;;;; target SBCL, but which can't be defined on the target until until
4 ;;;; some significant amount of machinery (e.g. error-handling) is
7 ;;;; This software is part of the SBCL system. See the README file for
10 ;;;; This software is derived from the CMU CL system, which was
11 ;;;; written at Carnegie Mellon University and released into the
12 ;;;; public domain. The software is in the public domain and is
13 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
14 ;;;; files for more information.
16 (in-package "SB!IMPL")
18 ;;; Is X a list for which LENGTH is meaningful, i.e. a list which is
19 ;;; not improper and which is not circular?
20 (defun list-with-length-p (x)
21 (values (ignore-errors (list-length x))))
23 ;;; not used in 0.7.8, but possibly useful for defensive programming
24 ;;; in e.g. (COERCE ... 'VECTOR)
25 ;;;(defun list-length-or-die (x)
26 ;;; (or (list-length x)
27 ;;; ;; not clear how to do this best:
28 ;;; ;; * Should this be a TYPE-ERROR? Colloquially that'd make
29 ;;; ;; lots of sense, but since I'm not sure how to express
30 ;;; ;; "noncircular list" as a Lisp type expression, coding
31 ;;; ;; it seems awkward.
32 ;;; ;; * Should the ERROR object include the offending value?
33 ;;; ;; Ordinarily that's helpful, but if the user doesn't have
34 ;;; ;; his printer set up to deal with cyclicity, we might not
35 ;;; ;; be doing him a favor by printing the object here.
36 ;;; ;; -- WHN 2002-10-19
37 ;;; (error "can't calculate length of cyclic list")))
39 ;;; This is used in constructing arg lists for debugger printing,
40 ;;; and when needing to print unbound slots in PCL.
41 (defstruct (unprintable-object
42 (:constructor make-unprintable-object (string))
43 (:print-object (lambda (x s)
44 (print-unreadable-object (x s)
45 (write-string (unprintable-object-string x) s))))
49 ;;; Used internally, but it would be nice to provide something
50 ;;; like this for users as well.
52 ;;; FIXME / IMPORTANT: If the slot is raw, the address is correct only for
53 ;;; instances of the specified class, not its subclasses!
55 (defmacro define-structure-slot-addressor (name &key structure slot)
56 (let* ((dd (find-defstruct-description structure t))
57 (slotd (when dd (find slot (dd-slots dd) :key #'dsd-name)))
58 (index (when slotd (dsd-index slotd)))
59 (raw-type (dsd-raw-type slotd)))
61 (error "Slot ~S not found in ~S." slot structure))
63 (declaim (inline ,name))
64 (defun ,name (instance)
65 (declare (type ,structure instance) (optimize speed))
68 (+ (sb!kernel:get-lisp-obj-address instance)
69 (- (* ,(if (eq t raw-type)
70 (+ sb!vm:instance-slots-offset index)
71 (- (1+ (sb!kernel::dd-instance-length dd)) sb!vm:instance-slots-offset index
72 (1- (sb!kernel::raw-slot-words raw-type))))
74 sb!vm:instance-pointer-lowtag)))))))
76 ;;;; ATOMIC-INCF and ATOMIC-DECF
78 (defun expand-atomic-frob (name place diff)
79 (flet ((invalid-place ()
80 (error "Invalid first argument to ~S: ~S" name place)))
83 (destructuring-bind (op &rest args) place
88 #!+(or x86 x86-64 ppc)
89 (with-unique-names (array)
90 `(let ((,array (the (simple-array sb!ext:word (*)) ,(car args))))
91 (%array-atomic-incf/word
93 (%check-bound ,array (array-dimension ,array 0) ,(cadr args))
94 (logand #.(1- (ash 1 sb!vm:n-word-bits))
97 `(the sb!vm:signed-word ,diff))
99 `(- (the sb!vm:signed-word ,diff))))))))
100 #!-(or x86 x86-64 ppc)
101 (with-unique-names (array index old-value)
102 (let ((incremented-value
105 `(+ ,old-value (the sb!vm:signed-word ,diff)))
107 `(- ,old-value (the sb!vm:signed-word ,diff))))))
108 `(sb!sys:without-interrupts
109 (let* ((,array ,(car args))
110 (,index ,(cadr args))
111 (,old-value (aref ,array ,index)))
112 (setf (aref ,array ,index)
113 (logand #.(1- (ash 1 sb!vm:n-word-bits))
119 (let ((dd (info :function :structure-accessor op)))
121 (let* ((structure (dd-name dd))
122 (slotd (find op (dd-slots dd) :key #'dsd-accessor-name))
123 (index (dsd-index slotd))
124 (type (dsd-type slotd)))
125 (declare (ignorable structure index))
126 (unless (and (eq 'sb!vm:word (dsd-raw-type slotd))
127 (type= (specifier-type type) (specifier-type 'sb!vm:word)))
128 (error "~S requires a slot of type (UNSIGNED-BYTE ~S), not ~S: ~S"
129 name sb!vm:n-word-bits type place))
130 (when (dsd-read-only slotd)
131 (error "Cannot use ~S with structure accessor for a read-only slot: ~S"
133 #!+(or x86 x86-64 ppc)
134 `(truly-the sb!vm:word
135 (%raw-instance-atomic-incf/word
136 (the ,structure ,@args) ,index
137 (logand #.(1- (ash 1 sb!vm:n-word-bits))
140 `(the sb!vm:signed-word ,diff))
142 `(- (the sb!vm:signed-word ,diff)))))))
143 ;; No threads outside x86 and x86-64 for now, so this is easy...
144 #!-(or x86 x86-64 ppc)
145 (with-unique-names (structure old)
146 `(sb!sys:without-interrupts
147 (let* ((,structure ,@args)
148 (,old (,op ,structure)))
149 (setf (,op ,structure)
150 (logand #.(1- (ash 1 sb!vm:n-word-bits))
153 `(+ ,old (the sb!vm:signed-word ,diff)))
155 `(- ,old (the sb!vm:signed-word ,diff))))))
157 (invalid-place))))))))
159 (defmacro atomic-incf (place &optional (diff 1))
161 "Atomically increments PLACE by DIFF, and returns the value of PLACE before
164 The incrementation is done using word-size modular arithmetic: on 32 bit
165 platforms ATOMIC-INCF of #xFFFFFFFF by one results in #x0 being stored in
168 PLACE must be an accessor form whose CAR is the name of a DEFSTRUCT accessor
169 whose declared type is (UNSIGNED-BYTE 32) on 32 bit platforms,
170 and (UNSIGNED-BYTE 64) on 64 bit platforms or an AREF of a (SIMPLE-ARRAY
171 SB-EXT:WORD (*) -- the type SB-EXT:WORD can be used for this purpose.
173 DIFF defaults to 1, and must be a (SIGNED-BYTE 32) on 32 bit platforms,
174 and (SIGNED-BYTE 64) on 64 bit platforms.
176 EXPERIMENTAL: Interface subject to change."
177 (expand-atomic-frob 'atomic-incf place diff))
179 (defmacro atomic-decf (place &optional (diff 1))
181 "Atomically decrements PLACE by DIFF, and returns the value of PLACE before
184 The decrementation is done using word-size modular arithmetic: on 32 bit
185 platforms ATOMIC-DECF of #x0 by one results in #xFFFFFFFF being stored in
188 PLACE must be an accessor form whose CAR is the name of a DEFSTRUCT accessor
189 whose declared type is (UNSIGNED-BYTE 32) on 32 bit platforms,
190 and (UNSIGNED-BYTE 64) on 64 bit platforms or an AREF of a (SIMPLE-ARRAY
191 SB-EXT:WORD (*) -- the type SB-EXT:WORD can be used for this purpose.
193 DIFF defaults to 1, and must be a (SIGNED-BYTE 32) on 32 bit platforms,
194 and (SIGNED-BYTE 64) on 64 bit platforms.
196 EXPERIMENTAL: Interface subject to change."
197 (expand-atomic-frob 'atomic-decf place diff))
199 ;; Interpreter stubs for ATOMIC-INCF.
200 #!+(or x86 x86-64 ppc)
201 (defun %array-atomic-incf/word (array index diff)
202 (declare (type (simple-array word (*)) array)
204 (type sb!vm:signed-word diff))
205 (%array-atomic-incf/word array index diff))
207 (defun spin-loop-hint ()
209 "Hints the processor that the current thread is spin-looping."
212 (defun call-hooks (kind hooks &key (on-error :error))
216 (serious-condition (c)
217 (if (eq :warn on-error)
218 (warn "Problem running ~A hook ~S:~% ~A" kind hook c)
219 (with-simple-restart (continue "Skip this ~A hook." kind)
220 (error "Problem running ~A hook ~S:~% ~A" kind hook c)))))))
224 (defmacro-mundanely defglobal (name value &optional (doc nil docp))
226 "Defines NAME as a global variable that is always bound. VALUE is evaluated
227 and assigned to NAME both at compile- and load-time, but only if NAME is not
230 Global variables share their values between all threads, and cannot be
231 locally bound, declared special, defined as constants, and neither bound
232 nor defined as symbol macros.
234 See also the declarations SB-EXT:GLOBAL and SB-EXT:ALWAYS-BOUND."
236 (eval-when (:compile-toplevel)
237 (let ((boundp (boundp ',name)))
238 (%compiler-defglobal ',name (unless boundp ,value) boundp)))
239 (eval-when (:load-toplevel :execute)
240 (let ((boundp (boundp ',name)))
241 (%defglobal ',name (unless boundp ,value) boundp ',doc ,docp
242 (sb!c:source-location))))))
244 (defun %compiler-defglobal (name value boundp)
245 (sb!xc:proclaim `(global ,name))
248 (set-symbol-global-value name value)
251 (sb!xc:proclaim `(always-bound ,name)))
253 (defun %defglobal (name value boundp doc docp source-location)
254 (%compiler-defglobal name value boundp)
256 (setf (fdocumentation name 'variable) doc))
257 (sb!c:with-source-location (source-location)
258 (setf (info :source-location :variable name) source-location))
261 ;;;; WAIT-FOR -- waiting on arbitrary conditions
263 (defun %%wait-for (test stop-sec stop-usec)
264 (declare (function test))
266 (declare (optimize (safety 0)))
267 (awhen (funcall test)
268 (return-from %%wait-for it)))
270 (declare (fixnum sec usec))
271 ;; TICK is microseconds
272 (+ usec (* 1000000 sec)))
274 (multiple-value-call #'tick
275 (decode-internal-time (get-internal-real-time)))))
276 (let* ((timeout-tick (when stop-sec (tick stop-sec stop-usec)))
278 ;; Rough estimate of how long a single attempt takes.
281 (max 1 (truncate (- (get-tick) start) 3)))))
282 ;; Scale sleeping between attempts:
284 ;; Start by sleeping for as many ticks as an average attempt
285 ;; takes, then doubling for each attempt.
287 ;; Max out at 0.1 seconds, or the 2 x time of a single try,
288 ;; whichever is longer -- with a hard cap of 10 seconds.
290 ;; FIXME: Maybe the API should have a :MAX-SLEEP argument?
291 (loop with max-ticks = (max 100000 (min (* 2 try-ticks)
293 for scale of-type fixnum = 1
294 then (let ((x (logand most-positive-fixnum (* 2 scale))))
299 (let* ((now (get-tick))
300 (sleep-ticks (min (* try-ticks scale) max-ticks))
303 ;; If sleep would take us past the
304 ;; timeout, shorten it so it's just
306 (if (>= (+ now sleep-ticks) timeout-tick)
310 (declare (fixnum sleep))
312 ;; microseconds to seconds and nanoseconds
313 (multiple-value-bind (sec nsec)
314 (truncate (* 1000 sleep) (expt 10 9))
316 (sb!unix:nanosleep sec nsec))))
318 (return-from %%wait-for nil))))))))
320 (defun %wait-for (test timeout)
321 (declare (function test))
324 (multiple-value-bind (to-sec to-usec stop-sec stop-usec deadlinep)
325 (decode-timeout timeout)
326 (declare (ignore to-sec to-usec))
327 (return-from %wait-for
328 (or (%%wait-for test stop-sec stop-usec)
333 (defmacro wait-for (test-form &key timeout)
335 "Wait until TEST-FORM evaluates to true, then return its primary value.
336 If TIMEOUT is provided, waits at most approximately TIMEOUT seconds before
339 If WITH-DEADLINE has been used to provide a global deadline, signals a
340 DEADLINE-TIMEOUT if TEST-FORM doesn't evaluate to true before the
343 Experimental: subject to change without prior notice."
344 `(dx-flet ((wait-for-test () (progn ,test-form)))
345 (%wait-for #'wait-for-test ,timeout)))
347 (defmacro with-progressive-timeout ((name &key seconds)
350 "Binds NAME as a local function for BODY. Each time #'NAME is called, it
351 returns SECONDS minus the time that has elapsed since BODY was entered, or
352 zero if more time than SECONDS has elapsed. If SECONDS is NIL, #'NAME
353 returns NIL each time."
354 (with-unique-names (deadline time-left sec)
355 `(let* ((,sec ,seconds)
358 (+ (get-internal-real-time)
359 (round (* ,seconds internal-time-units-per-second))))))
362 (let ((,time-left (- ,deadline (get-internal-real-time))))
363 (if (plusp ,time-left)
364 (* (coerce ,time-left 'single-float)
365 ,(/ 1.0 internal-time-units-per-second))
369 (defmacro atomic-update (place update-fn &rest arguments &environment env)
371 "Updates PLACE atomically to the value returned by calling function
372 designated by UPDATE-FN with ARGUMENTS and the previous value of PLACE.
374 PLACE may be read and UPDATE-FN evaluated and called multiple times before the
375 update succeeds: atomicity in this context means that value of place did not
376 change between the time it was read, and the time it was replaced with the
379 PLACE can be any place supported by SB-EXT:COMPARE-AND-SWAP.
383 ;;; Conses T to the head of FOO-LIST.
385 (defvar *foo* (make-foo))
386 (atomic-update (foo-list *foo*) #'cons t)
388 (let ((x (cons :count 0)))
389 (mapc #'sb-thread:join-thread
391 collect (sb-thread:make-thread
394 do (atomic-update (cdr x) #'1+)
396 ;; Guaranteed to be (:COUNT . 1000000) -- if you replace
397 ;; atomic update with (INCF (CDR X)) above, the result becomes
401 (multiple-value-bind (vars vals old new cas-form read-form)
402 (get-cas-expansion place env)
403 `(let* (,@(mapcar 'list vars vals)
405 (loop for ,new = (funcall ,update-fn ,@arguments ,old)
406 until (eq ,old (setf ,old ,cas-form))
407 finally (return ,new)))))
409 (defmacro atomic-push (obj place &environment env)
411 "Like PUSH, but atomic. PLACE may be read multiple times before
412 the operation completes -- the write does not occur until such time
413 that no other thread modified PLACE between the read and the write.
415 Works on all CASable places."
416 (multiple-value-bind (vars vals old new cas-form read-form)
417 (get-cas-expansion place env)
418 `(let* (,@(mapcar 'list vars vals)
420 (,new (cons ,obj ,old)))
421 (loop until (eq ,old (setf ,old ,cas-form))
422 do (setf (cdr ,new) ,old)
423 finally (return ,new)))))
425 (defmacro atomic-pop (place &environment env)
427 "Like POP, but atomic. PLACE may be read multiple times before
428 the operation completes -- the write does not occur until such time
429 that no other thread modified PLACE between the read and the write.
431 Works on all CASable places."
432 (multiple-value-bind (vars vals old new cas-form read-form)
433 (get-cas-expansion place env)
434 `(let* (,@(mapcar 'list vars vals))
435 (loop for ,old = ,read-form
436 for ,new = (cdr ,old)
437 until (eq ,old (setf ,old ,cas-form))
438 finally (return (car ,old))))))