Ifdef-ize the hopscotch hash stuff for non-x86.
[sbcl.git] / src / code / late-extensions.lisp
blob1b3a814ccd1599a422d8429fa962bedc2135bd7c
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
5 ;;;; defined
7 ;;;; This software is part of the SBCL system. See the README file for
8 ;;;; more information.
9 ;;;;
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 ;;; FIXME: the reason this can't be defined adjacent to its friends
21 ;;; PROPER-LIST-P and PROPER-LIST-OF-LENGTH-P is that HANDLER-BIND
22 ;;; does not work in 'primordial-extensions'.
23 (defun list-with-length-p (x)
24 (values (ignore-errors (list-length x))))
26 ;;; not used in 0.7.8, but possibly useful for defensive programming
27 ;;; in e.g. (COERCE ... 'VECTOR)
28 ;;;(defun list-length-or-die (x)
29 ;;; (or (list-length x)
30 ;;; ;; not clear how to do this best:
31 ;;; ;; * Should this be a TYPE-ERROR? Colloquially that'd make
32 ;;; ;; lots of sense, but since I'm not sure how to express
33 ;;; ;; "noncircular list" as a Lisp type expression, coding
34 ;;; ;; it seems awkward.
35 ;;; ;; * Should the ERROR object include the offending value?
36 ;;; ;; Ordinarily that's helpful, but if the user doesn't have
37 ;;; ;; his printer set up to deal with cyclicity, we might not
38 ;;; ;; be doing him a favor by printing the object here.
39 ;;; ;; -- WHN 2002-10-19
40 ;;; (error "can't calculate length of cyclic list")))
42 ;;; This is used in constructing arg lists for debugger printing,
43 ;;; and when needing to print unbound slots in PCL.
44 (defstruct (unprintable-object
45 (:constructor make-unprintable-object (string))
46 (:print-object (lambda (x s)
47 (print-unreadable-object (x s)
48 (write-string (unprintable-object-string x) s))))
49 (:copier nil))
50 (string nil :read-only t))
52 ;;; Used internally, but it would be nice to provide something
53 ;;; like this for users as well.
54 (defmacro define-structure-slot-addressor (name &key structure slot)
55 (let* ((dd (find-defstruct-description structure t))
56 (slotd (or (and dd (find slot (dd-slots dd) :key #'dsd-name))
57 (error "Slot ~S not found in ~S." slot structure)))
58 (index (dsd-index slotd)))
59 `(progn
60 (declaim (inline ,name))
61 (defun ,name (instance)
62 (declare (type ,structure instance) (optimize speed))
63 (truly-the
64 word
65 (+ (get-lisp-obj-address instance)
66 ,(+ (- sb!vm:instance-pointer-lowtag)
67 (* (+ sb!vm:instance-slots-offset index)
68 sb!vm:n-word-bytes))))))))
70 (defun spin-loop-hint ()
71 "Hints the processor that the current thread is spin-looping."
72 (spin-loop-hint))
74 (defun call-hooks (kind hooks &key (on-error :error))
75 (dolist (hook hooks)
76 (handler-case
77 (funcall hook)
78 (serious-condition (c)
79 (if (eq :warn on-error)
80 (warn "Problem running ~A hook ~S:~% ~A" kind hook c)
81 (with-simple-restart (continue "Skip this ~A hook." kind)
82 (error "Problem running ~A hook ~S:~% ~A" kind hook c)))))))
84 ;;;; DEFGLOBAL
86 (sb!xc:defmacro defglobal (name value &optional (doc nil docp))
87 "Defines NAME as a global variable that is always bound. VALUE is evaluated
88 and assigned to NAME both at compile- and load-time, but only if NAME is not
89 already bound.
91 Global variables share their values between all threads, and cannot be
92 locally bound, declared special, defined as constants, and neither bound
93 nor defined as symbol macros.
95 See also the declarations SB-EXT:GLOBAL and SB-EXT:ALWAYS-BOUND."
96 ;; Maybe kill docstring, but only under the cross-compiler.
97 #!+(and (not sb-doc) (host-feature sb-xc-host)) (setq doc nil)
98 (let ((boundp (make-symbol "BOUNDP")))
99 `(progn
100 (eval-when (:compile-toplevel)
101 (let ((,boundp (boundp ',name)))
102 (%compiler-defglobal ',name :always-bound
103 (unless ,boundp ,value) (not ,boundp))))
104 (let ((,boundp (boundp ',name)))
105 (%defglobal ',name (unless ,boundp ,value) ,boundp ',doc ,docp
106 (sb!c:source-location))))))
108 (sb!xc:defmacro define-load-time-global (name value &optional (doc nil docp))
109 "Defines NAME as a global variable that is always bound. VALUE is evaluated
110 and assigned to NAME at load-time, but only if NAME is not already bound.
112 Attempts to read NAME at compile-time will signal an UNBOUND-VARIABLE error
113 unless it has otherwise been assigned a value.
115 See also DEFGLOBAL which assigns the VALUE at compile-time too."
116 ;; Maybe kill docstring, but only under the cross-compiler.
117 #!+(and (not sb-doc) (host-feature sb-xc-host)) (setq doc nil)
118 (let ((boundp (make-symbol "BOUNDP")))
119 `(progn
120 (eval-when (:compile-toplevel)
121 (%compiler-defglobal ',name :eventually nil nil))
122 (let ((,boundp (boundp ',name)))
123 (%defglobal ',name (unless ,boundp ,value) ,boundp ',doc ,docp
124 (sb!c:source-location))))))
126 (defun %compiler-defglobal (name always-boundp value assign-it-p)
127 (sb!xc:proclaim `(global ,name))
128 (when assign-it-p
129 #-sb-xc-host
130 (set-symbol-global-value name value)
131 #+sb-xc-host
132 (set name value))
133 (sb!c::process-variable-declaration
134 name 'always-bound
135 ;; don't "weaken" the proclamation if it's in fact always bound now
136 (if (eq (info :variable :always-bound name) :always-bound)
137 :always-bound
138 always-boundp)))
140 (defun %defglobal (name value boundp doc docp source-location)
141 (%compiler-defglobal name :always-bound value (not boundp))
142 (when docp
143 (setf (fdocumentation name 'variable) doc))
144 (when source-location
145 (setf (info :source-location :variable name) source-location))
146 name)
148 ;;;; WAIT-FOR -- waiting on arbitrary conditions
150 #-sb-xc-host
151 (defun %%wait-for (test stop-sec stop-usec)
152 (declare (function test))
153 (labels ((try ()
154 (declare (optimize (safety 0)))
155 (awhen (funcall test)
156 (return-from %%wait-for it)))
157 (tick (sec usec)
158 (declare (type fixnum sec usec))
159 ;; TICK is microseconds
160 (+ usec (* 1000000 sec)))
161 (get-tick ()
162 (multiple-value-call #'tick
163 (decode-internal-time (get-internal-real-time)))))
164 (let* ((timeout-tick (when stop-sec (tick stop-sec stop-usec)))
165 (start (get-tick))
166 ;; Rough estimate of how long a single attempt takes.
167 (try-ticks (progn
168 (try) (try) (try)
169 (max 1 (truncate (- (get-tick) start) 3)))))
170 ;; Scale sleeping between attempts:
172 ;; Start by sleeping for as many ticks as an average attempt
173 ;; takes, then doubling for each attempt.
175 ;; Max out at 0.1 seconds, or the 2 x time of a single try,
176 ;; whichever is longer -- with a hard cap of 10 seconds.
178 ;; FIXME: Maybe the API should have a :MAX-SLEEP argument?
179 (loop with max-ticks = (max 100000 (min (* 2 try-ticks)
180 (expt 10 7)))
181 for scale of-type fixnum = 1
182 then (let ((x (logand most-positive-fixnum (* 2 scale))))
183 (if (> scale x)
184 most-positive-fixnum
186 do (try)
187 (let* ((now (get-tick))
188 (sleep-ticks (min (* try-ticks scale) max-ticks))
189 (sleep
190 (if timeout-tick
191 ;; If sleep would take us past the
192 ;; timeout, shorten it so it's just
193 ;; right.
194 (if (>= (+ now sleep-ticks) timeout-tick)
195 (- timeout-tick now)
196 sleep-ticks)
197 sleep-ticks)))
198 (declare (type fixnum sleep))
199 (cond ((plusp sleep)
200 ;; microseconds to seconds and nanoseconds
201 (multiple-value-bind (sec nsec)
202 (truncate (* 1000 sleep) (expt 10 9))
203 (with-interrupts
204 (sb!unix:nanosleep sec nsec))))
206 (return-from %%wait-for nil))))))))
208 #-sb-xc-host
209 (defun %wait-for (test timeout)
210 (declare (function test))
211 (tagbody
212 :restart
213 (multiple-value-bind (to-sec to-usec stop-sec stop-usec deadlinep)
214 (decode-timeout timeout)
215 (declare (ignore to-sec to-usec))
216 (return-from %wait-for
217 (or (%%wait-for test stop-sec stop-usec)
218 (when deadlinep
219 (signal-deadline)
220 (go :restart)))))))
222 (defmacro wait-for (test-form &key timeout)
223 "Wait until TEST-FORM evaluates to true, then return its primary value.
224 If TIMEOUT is provided, waits at most approximately TIMEOUT seconds before
225 returning NIL.
227 If WITH-DEADLINE has been used to provide a global deadline, signals a
228 DEADLINE-TIMEOUT if TEST-FORM doesn't evaluate to true before the
229 deadline.
231 Experimental: subject to change without prior notice."
232 `(dx-flet ((wait-for-test () (progn ,test-form)))
233 (%wait-for #'wait-for-test ,timeout)))
235 (defmacro with-progressive-timeout ((name &key seconds)
236 &body body)
237 "Binds NAME as a local function for BODY. Each time #'NAME is called, it
238 returns SECONDS minus the time that has elapsed since BODY was entered, or
239 zero if more time than SECONDS has elapsed. If SECONDS is NIL, #'NAME
240 returns NIL each time."
241 (with-unique-names (deadline time-left sec)
242 `(let* ((,sec ,seconds)
243 (,deadline
244 (when ,sec
245 (+ (get-internal-real-time)
246 (round (* ,seconds internal-time-units-per-second))))))
247 (flet ((,name ()
248 (when ,deadline
249 (let ((,time-left (- ,deadline (get-internal-real-time))))
250 (if (plusp ,time-left)
251 (* (coerce ,time-left 'single-float)
252 (load-time-value (/ 1.0f0 internal-time-units-per-second) t))
253 0)))))
254 ,@body))))
256 (defun split-version-string (string)
257 (loop with subversion and start = 0
258 with end = (length string)
259 when (setf (values subversion start)
260 (parse-integer string :start start :junk-allowed t))
261 collect it
262 while (and subversion
263 (< start end)
264 (char= (char string start) #\.))
265 do (incf start)))
267 (defun version>= (x y)
268 (unless (or x y)
269 (return-from version>= t))
270 (let ((head-x (or (first x) 0))
271 (head-y (or (first y) 0)))
272 (or (> head-x head-y)
273 (and (= head-x head-y)
274 (version>= (rest x) (rest y))))))
276 (defun assert-version->= (&rest subversions)
277 "Asserts that the current SBCL is of version equal to or greater than
278 the version specified in the arguments. A continuable error is signaled
279 otherwise.
281 The arguments specify a sequence of subversion numbers in big endian order.
282 They are compared lexicographically with the runtime version, and versions
283 are treated as though trailed by an unbounded number of 0s.
285 For example, (assert-version->= 1 1 4) asserts that the current SBCL is
286 version 1.1.4[.0.0...] or greater, and (assert-version->= 1) that it is
287 version 1[.0.0...] or greater."
288 (let ((version (split-version-string (lisp-implementation-version))))
289 (unless (version>= version subversions)
290 (cerror "Disregard this version requirement."
291 "SBCL ~A is too old for this program (version ~{~A~^.~} ~
292 or later is required)."
293 (lisp-implementation-version)
294 subversions))))
296 ;;; Signalling an error when trying to print an error condition is
297 ;;; generally a PITA, so whatever the failure encountered when
298 ;;; wondering about FILE-POSITION within a condition printer, 'tis
299 ;;; better silently to give up than to try to complain.
300 (defun file-position-or-nil-for-error (stream &optional (pos nil posp))
301 ;; Arguably FILE-POSITION shouldn't be signalling errors at all; but
302 ;; "NIL if this cannot be determined" in the ANSI spec doesn't seem
303 ;; absolutely unambiguously to prohibit errors when, e.g., STREAM
304 ;; has been closed so that FILE-POSITION is a nonsense question. So
305 ;; my (WHN) impression is that the conservative approach is to
306 ;; IGNORE-ERRORS. (I encountered this failure from within a homebrew
307 ;; defsystemish operation where the ERROR-STREAM had been CL:CLOSEd,
308 ;; I think by nonlocally exiting through a WITH-OPEN-FILE, by the
309 ;; time an error was reported.)
310 (ignore-errors
311 (if posp
312 (file-position stream pos)
313 (file-position stream))))
315 (defun stream-error-position-info (stream &optional position)
316 (when (and (not position) (form-tracking-stream-p stream))
317 (let ((line/col (line/col-from-charpos stream)))
318 (return-from stream-error-position-info
319 `((:line ,(car line/col))
320 (:column ,(cdr line/col))
321 ,@(let ((position (file-position-or-nil-for-error stream)))
322 ;; FIXME: 1- is technically broken for multi-byte external
323 ;; encodings, albeit bug-compatible with the broken code in
324 ;; the general case (below) for non-form-tracking-streams.
325 ;; i.e. If you position to this byte, it might not be the
326 ;; first byte of any character.
327 (when position `((:file-position ,(1- position)))))))))
329 ;; Give up early for interactive streams and non-character stream.
330 (when (or (ignore-errors (interactive-stream-p stream))
331 (not (subtypep (ignore-errors (stream-element-type stream))
332 'character)))
333 (return-from stream-error-position-info))
335 (flet ((read-content (old-position position)
336 "Read the content of STREAM into a buffer in order to count
337 lines and columns."
338 (unless (and old-position position
339 (< position sb!xc:array-dimension-limit))
340 (return-from read-content))
341 (let ((content
342 (make-string position :element-type (stream-element-type stream))))
343 (when (and (file-position-or-nil-for-error stream :start)
344 (eql position (ignore-errors (read-sequence content stream))))
345 (file-position-or-nil-for-error stream old-position)
346 content)))
347 ;; Lines count from 1, columns from 0. It's stupid and
348 ;; traditional.
349 (line (string)
350 (1+ (count #\Newline string)))
351 (column (string position)
352 (- position (or (position #\Newline string :from-end t) 0))))
353 (let* ((stream-position (file-position-or-nil-for-error stream))
354 (position (or position
355 ;; FILE-POSITION is the next character --
356 ;; error is at the previous one.
357 (and stream-position (plusp stream-position)
358 (1- stream-position))))
359 (content (read-content stream-position position)))
360 `(,@(when content `((:line ,(line content))
361 (:column ,(column content position))))
362 ,@(when position `((:file-position ,position)))))))