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 (+ (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
))
72 sb
!vm
:instance-slots-offset index
73 (1- (sb!kernel
::raw-slot-words raw-type
))))
75 sb
!vm
:instance-pointer-lowtag
)))))))
77 ;;;; ATOMIC-INCF and ATOMIC-DECF
79 (defun expand-atomic-frob (name specified-place diff env
80 &aux
(place (sb!xc
:macroexpand specified-place env
)))
81 (declare (type (member atomic-incf atomic-decf
) name
))
82 (flet ((invalid-place ()
83 (error "Invalid first argument to ~S: ~S" name specified-place
)))
84 (if (and (symbolp place
)
85 (eq (info :variable
:kind place
) :global
)
86 (type= (info :variable
:type place
) (specifier-type 'fixnum
)))
87 ;; Global can't be lexically rebound.
88 (return-from expand-atomic-frob
89 `(truly-the fixnum
(,(case name
90 (atomic-incf '%atomic-inc-symbol-global-value
)
91 (atomic-decf '%atomic-dec-symbol-global-value
))
92 ',place
(the fixnum
,diff
)))))
96 (destructuring-bind (op &rest args
) place
97 ;; FIXME: The lexical environment should not be disregarded.
98 ;; CL builtins can't be lexically rebound, but structure accessors can.
103 #!+(or x86 x86-64 ppc
)
104 (with-unique-names (array)
105 `(let ((,array
(the (simple-array word
(*)) ,(car args
))))
106 (%array-atomic-incf
/word
108 (%check-bound
,array
(array-dimension ,array
0) ,(cadr args
))
109 (logand #.
(1- (ash 1 sb
!vm
:n-word-bits
))
111 (atomic-incf `(the sb
!vm
:signed-word
,diff
))
112 (atomic-decf `(- (the sb
!vm
:signed-word
,diff
))))))))
113 #!-
(or x86 x86-64 ppc
)
114 (with-unique-names (array index old-value
)
115 (let ((incremented-value
118 `(+ ,old-value
(the sb
!vm
:signed-word
,diff
)))
120 `(- ,old-value
(the sb
!vm
:signed-word
,diff
))))))
122 (let* ((,array
,(car args
))
123 (,index
,(cadr args
))
124 (,old-value
(aref ,array
,index
)))
125 (setf (aref ,array
,index
)
126 (logand #.
(1- (ash 1 sb
!vm
:n-word-bits
))
129 ((car cdr first rest
)
135 ((first car
) (case name
136 (atomic-incf '%atomic-inc-car
)
137 (atomic-decf '%atomic-dec-car
)))
138 ((rest cdr
) (case name
139 (atomic-incf '%atomic-inc-cdr
)
140 (atomic-decf '%atomic-dec-cdr
))))
141 ,(car args
) (the fixnum
,diff
))))
145 (let ((dd (info :function
:structure-accessor op
)))
147 (let* ((structure (dd-name dd
))
148 (slotd (find op
(dd-slots dd
) :key
#'dsd-accessor-name
))
149 (index (dsd-index slotd
))
150 (type (dsd-type slotd
)))
151 (declare (ignorable structure index
))
152 (unless (and (eq 'sb
!vm
:word
(dsd-raw-type slotd
))
153 (type= (specifier-type type
) (specifier-type 'sb
!vm
:word
)))
154 (error "~S requires a slot of type (UNSIGNED-BYTE ~S), not ~S: ~S"
155 name sb
!vm
:n-word-bits type place
))
156 (when (dsd-read-only slotd
)
157 (error "Cannot use ~S with structure accessor for a read-only slot: ~S"
159 #!+(or x86 x86-64 ppc
)
160 `(truly-the sb
!vm
:word
161 (%raw-instance-atomic-incf
/word
162 (the ,structure
,@args
) ,index
163 (logand #.
(1- (ash 1 sb
!vm
:n-word-bits
))
166 `(the sb
!vm
:signed-word
,diff
))
168 `(- (the sb
!vm
:signed-word
,diff
)))))))
169 ;; No threads outside x86 and x86-64 for now, so this is easy...
170 #!-
(or x86 x86-64 ppc
)
171 (with-unique-names (structure old
)
173 (let* ((,structure
,@args
)
174 (,old
(,op
,structure
)))
175 (setf (,op
,structure
)
177 #.
(1- (ash 1 sb
!vm
:n-word-bits
))
180 `(+ ,old
(the sb
!vm
:signed-word
,diff
)))
182 `(- ,old
(the sb
!vm
:signed-word
,diff
))))))
184 (invalid-place))))))))
186 (def!macro atomic-incf
(&environment env place
&optional
(diff 1))
189 "Atomically increments PLACE by DIFF, and returns the value of PLACE before
192 PLACE must access one of the following:
193 - a DEFSTRUCT slot with declared type (UNSIGNED-BYTE ~D~:*)
194 or AREF of a (SIMPLE-ARRAY (UNSIGNED-BYTE ~D~:*) (*))
195 The type SB-EXT:WORD can be used for these purposes.
196 - CAR or CDR (respectively FIRST or REST) of a CONS.
197 - a variable defined using DEFGLOBAL with a proclaimed type of FIXNUM.
198 Macroexpansion is performed on PLACE before expanding ATOMIC-INCF.
200 Incrementing is done using modular arithmetic,
201 which is well-defined over two different domains:
202 - For structures and arrays, the operation accepts and produces
203 an (UNSIGNED-BYTE ~D~:*), and DIFF must be of type (SIGNED-BYTE ~D).
204 ATOMIC-INCF of #x~x by one results in #x0 being stored in PLACE.
205 - For other places, the domain is FIXNUM, and DIFF must be a FIXNUM.
206 ATOMIC-INCF of #x~x by one results in #x~x
207 being stored in PLACE.
211 EXPERIMENTAL: Interface subject to change."
212 sb
!vm
:n-word-bits most-positive-word
213 most-positive-fixnum most-negative-fixnum
)
214 (expand-atomic-frob 'atomic-incf place diff env
))
216 (defmacro atomic-decf
(&environment env place
&optional
(diff 1))
219 "Atomically decrements PLACE by DIFF, and returns the value of PLACE before
222 PLACE must access one of the following:
223 - a DEFSTRUCT slot with declared type (UNSIGNED-BYTE ~D~:*)
224 or AREF of a (SIMPLE-ARRAY (UNSIGNED-BYTE ~D~:*) (*))
225 The type SB-EXT:WORD can be used for these purposes.
226 - CAR or CDR (respectively FIRST or REST) of a CONS.
227 - a variable defined using DEFGLOBAL with a proclaimed type of FIXNUM.
228 Macroexpansion is performed on PLACE before expanding ATOMIC-DECF.
230 Decrementing is done using modular arithmetic,
231 which is well-defined over two different domains:
232 - For structures and arrays, the operation accepts and produces
233 an (UNSIGNED-BYTE ~D~:*), and DIFF must be of type (SIGNED-BYTE ~D).
234 ATOMIC-DECF of #x0 by one results in #x~x being stored in PLACE.
235 - For other places, the domain is FIXNUM, and DIFF must be a FIXNUM.
236 ATOMIC-DECF of #x~x by one results in #x~x
237 being stored in PLACE.
241 EXPERIMENTAL: Interface subject to change."
242 sb
!vm
:n-word-bits most-positive-word
243 most-negative-fixnum most-positive-fixnum
)
244 (expand-atomic-frob 'atomic-decf place diff env
))
246 ;; Interpreter stubs for ATOMIC-INCF.
247 #!+(or x86 x86-64 ppc
)
248 (defun %array-atomic-incf
/word
(array index diff
)
249 (declare (type (simple-array word
(*)) array
)
251 (type sb
!vm
:signed-word diff
))
252 (%array-atomic-incf
/word array index diff
))
254 ;; This code would be more concise if workable versions
255 ;; of +-MODFX, --MODFX were defined generically.
256 (macrolet ((modular (fun a b
)
258 `(,(let ((*package
* (find-package "SB!VM")))
259 (symbolicate fun
"-MODFX"))
262 ;; algorithm of https://graphics.stanford.edu/~seander/bithacks
263 `(let ((res (logand (,fun
,a
,b
)
264 (ash sb
!ext
:most-positive-word
265 (- sb
!vm
:n-fixnum-tag-bits
))))
266 (m (ash 1 (1- sb
!vm
:n-fixnum-bits
))))
267 (- (logxor res m
) m
))))
269 ;; Atomically frob a global variable.
270 ;; There is a quasi-bug that "can't happen" - the CAS operation will swap
271 ;; a thread-local value, however this function should only be called with
272 ;; a symbol that is known not to be thread-locally bindable.
273 ;; (We should define a CASser for SYMBOL-GLOBAL-VALUE)
274 (macrolet ((def-frob (name op
)
275 `(defun ,name
(symbol delta
)
276 (declare (symbol symbol
) (fixnum delta
))
277 (loop (let ((old (truly-the
279 (locally (declare (optimize (safety 0)))
280 (symbol-global-value symbol
)))))
281 (when (eq (%compare-and-swap-symbol-value
282 symbol old
(modular ,op old delta
))
285 (def-frob %atomic-inc-symbol-global-value
+)
286 (def-frob %atomic-dec-symbol-global-value -
))
288 ;; Atomically frob the CAR or CDR of a cons.
289 (macrolet ((def-frob (name op slot
)
290 `(defun ,name
(cell delta
)
291 (declare (cons cell
) (fixnum delta
))
292 (loop (let ((old (the fixnum
(,slot cell
))))
293 (when (eq (cas (,slot cell
) old
294 (modular ,op old delta
)) old
)
296 (def-frob %atomic-inc-car
+ car
)
297 (def-frob %atomic-dec-car - car
)
298 (def-frob %atomic-inc-cdr
+ cdr
)
299 (def-frob %atomic-dec-cdr - cdr
)))
301 (defun spin-loop-hint ()
303 "Hints the processor that the current thread is spin-looping."
306 (defun call-hooks (kind hooks
&key
(on-error :error
))
310 (serious-condition (c)
311 (if (eq :warn on-error
)
312 (warn "Problem running ~A hook ~S:~% ~A" kind hook c
)
313 (with-simple-restart (continue "Skip this ~A hook." kind
)
314 (error "Problem running ~A hook ~S:~% ~A" kind hook c
)))))))
318 (defmacro-mundanely defglobal
(name value
&optional
(doc nil docp
))
320 "Defines NAME as a global variable that is always bound. VALUE is evaluated
321 and assigned to NAME both at compile- and load-time, but only if NAME is not
324 Global variables share their values between all threads, and cannot be
325 locally bound, declared special, defined as constants, and neither bound
326 nor defined as symbol macros.
328 See also the declarations SB-EXT:GLOBAL and SB-EXT:ALWAYS-BOUND."
329 (let ((boundp (make-symbol "BOUNDP")))
331 (eval-when (:compile-toplevel
)
332 (let ((,boundp
(boundp ',name
)))
333 (%compiler-defglobal
',name
:always-bound
334 (unless ,boundp
,value
) (not ,boundp
))))
335 (let ((,boundp
(boundp ',name
)))
336 (%defglobal
',name
(unless ,boundp
,value
) ,boundp
',doc
,docp
337 (sb!c
:source-location
))))))
339 (defmacro-mundanely define-load-time-global
(name value
&optional
(doc nil docp
))
341 "Defines NAME as a global variable that is always bound. VALUE is evaluated
342 and assigned to NAME at load-time, but only if NAME is not already bound.
344 Attempts to read NAME at compile-time will signal an UNBOUND-VARIABLE error
345 unless it has otherwise been assigned a value.
347 See also DEFGLOBAL which assigns the VALUE at compile-time too."
348 (let ((boundp (make-symbol "BOUNDP")))
350 (eval-when (:compile-toplevel
)
351 (%compiler-defglobal
',name
:eventually nil nil
))
352 (let ((,boundp
(boundp ',name
)))
353 (%defglobal
',name
(unless ,boundp
,value
) ,boundp
',doc
,docp
354 (sb!c
:source-location
))))))
356 (defun %compiler-defglobal
(name always-boundp value assign-it-p
)
357 (sb!xc
:proclaim
`(global ,name
))
360 (set-symbol-global-value name value
)
363 (sb!c
::process-variable-declaration
365 ;; don't "weaken" the proclamation if it's in fact always bound now
366 (if (eq (info :variable
:always-bound name
) :always-bound
)
370 (defun %defglobal
(name value boundp doc docp source-location
)
371 (%compiler-defglobal name
:always-bound value
(not boundp
))
373 (setf (fdocumentation name
'variable
) doc
))
374 (sb!c
:with-source-location
(source-location)
375 (setf (info :source-location
:variable name
) source-location
))
378 ;;;; WAIT-FOR -- waiting on arbitrary conditions
380 (defun %%wait-for
(test stop-sec stop-usec
)
381 (declare (function test
))
383 (declare (optimize (safety 0)))
384 (awhen (funcall test
)
385 (return-from %%wait-for it
)))
387 (declare (fixnum sec usec
))
388 ;; TICK is microseconds
389 (+ usec
(* 1000000 sec
)))
391 (multiple-value-call #'tick
392 (decode-internal-time (get-internal-real-time)))))
393 (let* ((timeout-tick (when stop-sec
(tick stop-sec stop-usec
)))
395 ;; Rough estimate of how long a single attempt takes.
398 (max 1 (truncate (- (get-tick) start
) 3)))))
399 ;; Scale sleeping between attempts:
401 ;; Start by sleeping for as many ticks as an average attempt
402 ;; takes, then doubling for each attempt.
404 ;; Max out at 0.1 seconds, or the 2 x time of a single try,
405 ;; whichever is longer -- with a hard cap of 10 seconds.
407 ;; FIXME: Maybe the API should have a :MAX-SLEEP argument?
408 (loop with max-ticks
= (max 100000 (min (* 2 try-ticks
)
410 for scale of-type fixnum
= 1
411 then
(let ((x (logand most-positive-fixnum
(* 2 scale
))))
416 (let* ((now (get-tick))
417 (sleep-ticks (min (* try-ticks scale
) max-ticks
))
420 ;; If sleep would take us past the
421 ;; timeout, shorten it so it's just
423 (if (>= (+ now sleep-ticks
) timeout-tick
)
427 (declare (fixnum sleep
))
429 ;; microseconds to seconds and nanoseconds
430 (multiple-value-bind (sec nsec
)
431 (truncate (* 1000 sleep
) (expt 10 9))
433 (sb!unix
:nanosleep sec nsec
))))
435 (return-from %%wait-for nil
))))))))
437 (defun %wait-for
(test timeout
)
438 (declare (function test
))
441 (multiple-value-bind (to-sec to-usec stop-sec stop-usec deadlinep
)
442 (decode-timeout timeout
)
443 (declare (ignore to-sec to-usec
))
444 (return-from %wait-for
445 (or (%%wait-for test stop-sec stop-usec
)
450 (defmacro wait-for
(test-form &key timeout
)
452 "Wait until TEST-FORM evaluates to true, then return its primary value.
453 If TIMEOUT is provided, waits at most approximately TIMEOUT seconds before
456 If WITH-DEADLINE has been used to provide a global deadline, signals a
457 DEADLINE-TIMEOUT if TEST-FORM doesn't evaluate to true before the
460 Experimental: subject to change without prior notice."
461 `(dx-flet ((wait-for-test () (progn ,test-form
)))
462 (%wait-for
#'wait-for-test
,timeout
)))
464 (defmacro with-progressive-timeout
((name &key seconds
)
467 "Binds NAME as a local function for BODY. Each time #'NAME is called, it
468 returns SECONDS minus the time that has elapsed since BODY was entered, or
469 zero if more time than SECONDS has elapsed. If SECONDS is NIL, #'NAME
470 returns NIL each time."
471 (with-unique-names (deadline time-left sec
)
472 `(let* ((,sec
,seconds
)
475 (+ (get-internal-real-time)
476 (round (* ,seconds internal-time-units-per-second
))))))
479 (let ((,time-left
(- ,deadline
(get-internal-real-time))))
480 (if (plusp ,time-left
)
481 (* (coerce ,time-left
'single-float
)
482 ,(/ 1.0 internal-time-units-per-second
))
486 (defmacro atomic-update
(place update-fn
&rest arguments
&environment env
)
488 "Updates PLACE atomically to the value returned by calling function
489 designated by UPDATE-FN with ARGUMENTS and the previous value of PLACE.
491 PLACE may be read and UPDATE-FN evaluated and called multiple times before the
492 update succeeds: atomicity in this context means that value of place did not
493 change between the time it was read, and the time it was replaced with the
496 PLACE can be any place supported by SB-EXT:COMPARE-AND-SWAP.
500 ;;; Conses T to the head of FOO-LIST.
502 (defvar *foo* (make-foo))
503 (atomic-update (foo-list *foo*) #'cons t)
505 (let ((x (cons :count 0)))
506 (mapc #'sb-thread:join-thread
508 collect (sb-thread:make-thread
511 do (atomic-update (cdr x) #'1+)
513 ;; Guaranteed to be (:COUNT . 1000000) -- if you replace
514 ;; atomic update with (INCF (CDR X)) above, the result becomes
518 (multiple-value-bind (vars vals old new cas-form read-form
)
519 (get-cas-expansion place env
)
520 `(let* (,@(mapcar 'list vars vals
)
522 (loop for
,new
= (funcall ,update-fn
,@arguments
,old
)
523 until
(eq ,old
(setf ,old
,cas-form
))
524 finally
(return ,new
)))))
526 (defmacro atomic-push
(obj place
&environment env
)
528 "Like PUSH, but atomic. PLACE may be read multiple times before
529 the operation completes -- the write does not occur until such time
530 that no other thread modified PLACE between the read and the write.
532 Works on all CASable places."
533 (multiple-value-bind (vars vals old new cas-form read-form
)
534 (get-cas-expansion place env
)
535 `(let* (,@(mapcar 'list vars vals
)
537 (,new
(cons ,obj
,old
)))
538 (loop until
(eq ,old
(setf ,old
,cas-form
))
539 do
(setf (cdr ,new
) ,old
)
540 finally
(return ,new
)))))
542 (defmacro atomic-pop
(place &environment env
)
544 "Like POP, but atomic. PLACE may be read multiple times before
545 the operation completes -- the write does not occur until such time
546 that no other thread modified PLACE between the read and the write.
548 Works on all CASable places."
549 (multiple-value-bind (vars vals old new cas-form read-form
)
550 (get-cas-expansion place env
)
551 `(let* (,@(mapcar 'list vars vals
))
552 (loop for
,old
= ,read-form
553 for
,new
= (cdr ,old
)
554 until
(eq ,old
(setf ,old
,cas-form
))
555 finally
(return (car ,old
))))))
557 (defun split-version-string (string)
558 (loop with subversion and start
= 0
559 with end
= (length string
)
560 when
(setf (values subversion start
)
561 (parse-integer string
:start start
:junk-allowed t
))
563 while
(and subversion
565 (char= (char string start
) #\.
))
568 (defun version>= (x y
)
570 (return-from version
>= t
))
571 (let ((head-x (or (first x
) 0))
572 (head-y (or (first y
) 0)))
573 (or (> head-x head-y
)
574 (and (= head-x head-y
)
575 (version>= (rest x
) (rest y
))))))
577 (defun assert-version->= (&rest subversions
)
579 "Asserts that the current SBCL is of version equal to or greater than
580 the version specified in the arguments. A continuable error is signaled
583 The arguments specify a sequence of subversion numbers in big endian order.
584 They are compared lexicographically with the runtime version, and versions
585 are treated as though trailed by an unbounded number of 0s.
587 For example, (assert-version->= 1 1 4) asserts that the current SBCL is
588 version 1.1.4[.0.0...] or greater, and (assert-version->= 1) that it is
589 version 1[.0.0...] or greater."
590 (let ((version (split-version-string (lisp-implementation-version))))
591 (unless (version>= version subversions
)
592 (cerror "Disregard this version requirement."
593 "SBCL ~A is too old for this program (version ~{~A~^.~} ~
594 or later is required)."
595 (lisp-implementation-version)