1 ;;;; tests of the INFO/globaldb system
3 ;;;; KLUDGE: Unlike most of the system's tests, these are not in the
4 ;;;; problem domain, but in the implementation domain, so modification
5 ;;;; of the system could cause these tests to fail even if the system
6 ;;;; was still a correct implementation of ANSI Common Lisp + SBCL
7 ;;;; extensions. Perhaps such tests should be separate from tests in
8 ;;;; the problem domain. -- WHN 2001-02-11
10 ;;;; This software is part of the SBCL system. See the README file for
11 ;;;; more information.
13 ;;;; While most of SBCL is derived from the CMU CL system, the test
14 ;;;; files (like this one) were written from scratch after the fork
17 ;;;; This software is in the public domain and is provided with
18 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
19 ;;;; more information.
23 (test-util:with-test
(:name
:no-meta-info
)
24 (assert-signal (compile nil
'(lambda (x) (sb-int:info
:type
:nokind x
)))
27 (defun foo (a) (list a
))
30 (assert (eq (sb-int:info
:function
:where-from
'foo
)
33 (defun foo (a b
) (list a b
))
34 (let ((x 1)) (foo x
2))
40 ;;; FIXME: This one is commented out since it doesn't work when
41 ;;; the DEFUN is just LOADed instead of COMPILE-FILEd, and it's
42 ;;; not immediately obvious what's the best way to set up
43 ;;; the COMPILE-FILE test.
47 (format nil
"~A" (sb-int:proclaimed-ftype
'foo
))
48 "#<FUN-TYPE (FUNCTION (T T) LIST)>"))
51 (with-test (:name
:fboundp-type-error
)
52 (assert-error (funcall (compile nil
`(lambda (x) (fboundp x
))) 0)
54 (assert-error (funcall (compile nil
`(lambda (x) (fdefinition x
))) 0)
59 (test-util:with-test
(:name
:globaldb-sxhashoid-discrimination
)
60 (assert (not (eql (globaldb-sxhashoid '(a b c d e
))
61 (globaldb-sxhashoid '(a b c d mumble
))))))
63 (test-util:with-test
(:name
:bug-458015
)
64 ;; Make sure layouts have sane source-locations
65 (sb-c::call-with-each-globaldb-name
67 (when (and (symbolp info-name
) (info :type
:kind info-name
))
68 (let* ((classoid (find-classoid info-name nil
))
69 (layout (and classoid
(classoid-layout classoid
)))
70 (srcloc (and layout
(sb-kernel::layout-source-location layout
))))
72 (assert (or (definition-source-location-p srcloc
)
75 (test-util:with-test
(:name
:find-classoid-signal-error
)
76 ;; (EVAL ''SILLY) dumbs down the compiler for this test.
77 ;; FIND-CLASSOID on a constant symbol becomes
78 ;; `(CLASSOID-CELL-CLASSOID ',(FIND-CLASSOID-CELL name :create t))
79 ;; and I want just the primitive operations without muddying the water.
80 (let ((name (eval ''silly
)))
81 (assert (not (find-classoid name nil
)))
82 (assert (typep (handler-case (find-classoid name
) (error (e) e
)) 'error
))
83 (find-classoid-cell name
:create t
) ; After this, have cell but no classoid
84 (assert (typep (handler-case (find-classoid name
) (error (e) e
)) 'error
))))
86 (test-util:with-test
(:name
:set-info-value-type-check
)
87 (loop for type-info across
*info-types
*
88 when
(and type-info
(not (eq (meta-info-type-spec type-info
) 't
)))
90 (let ((key1 (meta-info-category type-info
))
91 (key2 (meta-info-kind type-info
))
92 (sillyval (make-string-output-stream))) ; nothing should be this
93 ;; check the type-checker function
96 (declare (notinline (setf info
)))
97 (setf (info ,key1
,key2
'grrr
) x
)))))
98 (assert (typep (nth-value 1 (ignore-errors (funcall f sillyval
)))
100 ;; Demonstrate that the SETF disallows the illegal value
101 ;; even though this lambda attempts to be non-type-safe.
102 (let ((f (compile nil
`(lambda (x)
103 (declare (optimize (safety 0)))
104 (setf (info ,key1
,key2
'grrr
) x
)))))
105 (assert (typep (nth-value 1 (ignore-errors (funcall f sillyval
)))
107 ;; but if I *really* want, a bad value can be installed
108 (set-info-value (gensym)
109 (meta-info-number (meta-info :variable
:kind
))
112 (test-util:with-test
(:name
:unrecognize-recognized-declaration
)
113 (proclaim '(declaration happiness
))
114 (let ((saved (copy-list *recognized-declarations
*)))
115 (assert (member 'happiness
*recognized-declarations
*))
116 (proclaim '(declaration happiness
))
117 (assert (equal *recognized-declarations
* saved
)) ; not pushed again
118 (setf (info :declaration
:recognized
'happiness
) nil
)
119 (assert (not (member 'happiness
*recognized-declarations
*)))))
121 (test-util:with-test
(:name
:recognized-decl-not-also-type
)
122 (deftype pear
(x) `(cons ,x
,x
))
123 (assert (typep (nth-value 1 (ignore-errors (proclaim '(declaration pear
))))
124 'declaration-type-conflict-error
))
125 (proclaim '(declaration nthing
))
126 (assert (typep (nth-value 1 (ignore-errors (deftype nthing
(x) `(not ,x
))))
127 'declaration-type-conflict-error
)))
129 (test-util:with-test
(:name
:info-env-clear
)
130 (setf (info :variable
:kind
'fruitbaskets
) :macro
131 (info :variable
:macro-expansion
'fruitbaskets
) 32)
132 (clear-info :variable
:kind
'fruitbaskets
)
133 (multiple-value-bind (data foundp
)
134 (info :variable
:kind
'fruitbaskets
)
135 (assert (and (eq data
:unknown
) (not foundp
))))
136 (multiple-value-bind (data foundp
)
137 (info :variable
:macro-expansion
'fruitbaskets
)
138 (assert (and foundp
(eql data
32))))
139 (clear-info :variable
:macro-expansion
'fruitbaskets
)
140 (multiple-value-bind (data foundp
)
141 (info :variable
:macro-expansion
'fruitbaskets
)
142 (assert (and (not foundp
) (not data
)))))
144 ;; packed info vector tests
146 (test-util:with-test
(:name
:globaldb-info-iterate
)
147 (let ((s (with-output-to-string (*standard-output
*) (show-info '*))))
148 (dolist (x '((:function
:definition
) (:function
:type
)
149 (:function
:where-from
) (:function
:kind
)
150 (:function
:info
) (:function
:source-transform
)
151 (:type
:kind
) (:type
:builtin
)
152 (:source-location
:declaration
)
154 #+sb-doc
(:variable
:documentation
)
155 (:variable
:type
) (:variable
:where-from
)
156 (:source-location
:variable
)
157 (:alien-type
:kind
) (:alien-type
:translator
)))
158 (assert (search (format nil
"~S ~S" (car x
) (cadr x
)) s
)))))
160 (test-util:with-test
(:name
:find-fdefn-agreement
)
161 ;; Shows that GET-INFO-VALUE agrees with FIND-FDEFN on all symbols,
162 ;; since they use diffent code. Something would have crashed long before here...
164 (assert (eq (find-fdefn x
) (info :function
:definition x
)))))
170 (defun shuffle (vector) ; destructive
171 (loop for lim from
(1- (length vector
)) downto
0
172 for chosen
= (random (1+ lim
))
173 unless
(= chosen lim
)
174 do
(rotatef (aref vector chosen
) (aref vector lim
)))
177 (test-util:with-test
(:name
:quick-packed-info-insert
)
178 ;; Exercise some bit patterns that touch the sign bit on 32-bit machines.
181 (let ((iv1 +nil-packed-infos
+)
182 (iv2 +nil-packed-infos
+)
184 (cons 1 (subseq '(#b100000
#b110000
#b010000
#b011000
185 #b000100
#b000010
#b000011
#b000001
)
186 0 (- +infos-per-word
+ 2)))))
187 ;; Randomize because maybe there's an ordering constraint more
188 ;; complicated than fdefn always getting to be first.
189 ;; (there isn't, but could be)
190 (dolist (num (coerce (shuffle (coerce type-nums
'vector
)) 'list
))
191 (let ((val (format nil
"value for ~D" num
)))
192 (setq iv1
(quick-packed-info-insert iv1 num val
)
193 iv2
(%packed-info-insert
; not PACKED-INFO-INSERT
194 iv2
+no-auxilliary-key
+ num val
))
195 (assert (equalp iv1 iv2
))))
196 ;; the first and only info descriptor should be full
197 (assert (not (info-quickly-insertable-p iv1
))))))
199 (defun crossprod (a b
)
200 (mapcan (lambda (x) (mapcar (lambda (y) (cons x y
)) b
))
203 ;; The real GET-INFO-VALUE AVERs that INFO-NUMBER is legal. This one doesn't.
204 (defun cheating-get-info-value (sym aux-key info-number
)
205 (let* ((vector (symbol-info-vector sym
))
206 (index (packed-info-value-index vector aux-key info-number
)))
208 (values (svref vector index
) t
)
211 ;; Info vectors may be concurrently updated. If more than one thread writes
212 ;; the same name/info-number, it's random which thread prevails, but for
213 ;; non-colliding updates, none should be lost.
214 ;; This is not a "reasonable" use of packed info vectors.
215 ;; It's just a check of the response of the algorithm to heavy pounding.
217 (test-util:with-test
(:name
:info-vector-concurrency
)
219 (a (make-array 1 :element-type
'sb-ext
:word
)))
220 (let* ((aux-keys '(0 a b c d e f g h nil i j k l m n o p setf q r s
))
221 (info-types (loop for i from
1 below
64 collect i
))
222 (work (shuffle (coerce (crossprod aux-keys info-types
) 'vector
)))
223 (n (floor (length work
) 4))
227 (sb-thread:make-thread
229 (loop for x across work
230 do
(set-info-value (if (eq (car x
) 0) s
`(,(car x
) ,s
))
232 (list (atomic-incf (aref a
0)) my-id
235 :arguments
(list (subseq work
(* i n
) (if (= i
3) nil
(* (1+ i
) n
)))
238 (dolist (thread threads
)
239 (sb-thread:join-thread thread
))
240 (let ((foo (make-array (aref a
0))))
241 ;; Returning FOO is to give a rough visual indication that
242 ;; there were in fact intermingled updates.
243 (dolist (k aux-keys foo
)
245 (let ((answer (cheating-get-info-value s k i
)))
247 (setf (aref foo
(car answer
)) answer
))
248 (assert (equal (third answer
)
250 (format nil
"~A,~A" k i
)))))))))))
252 ;; specialized concurrent hashtable tests
254 (defun integer-range (min max
)
255 (let* ((n (1+ (- max min
)))
258 (setf (aref a j
) (+ j min
)))))
260 (defun randomize (key random-state
)
262 (logior (ash key
10) (random (ash 1 10) random-state
))
263 key
)) ; not randomizing
265 (defun show-tally (table tally verb print
)
267 (format t
"Hashtable has ~D entries~%" (info-env-count table
)))
269 (dotimes (thread-id (length tally
) tot
)
270 (let ((n (aref tally thread-id
)))
272 (format t
"Thread ~2d ~A ~7d time~:P~%" thread-id verb n
))
275 (defun test-concurrent-incf (&key
(table (make-info-hashtable))
276 (n-threads 40) (n-inserts 50000)
278 (declare (optimize safety
))
280 (worklists (make-array n-threads
))
282 (tries (make-array n-threads
:initial-element
0)))
283 (dotimes (i n-threads
)
284 ;; Insert the integers [-n .. -2]. Keys 0 and -1 are illegal.
285 (setf (aref worklists i
)
286 (shuffle (integer-range (- (1+ n-inserts
)) -
2))))
287 (dotimes (i n-threads
)
288 (push (sb-thread:make-thread
289 (lambda (worklist me
)
290 (declare (simple-vector worklist
))
292 (incf (aref tries me
))
294 (declare (dynamic-extent #'doer
))
295 ;; for each item in worklist, increment that key
296 (loop for key across worklist do
297 (info-puthash table key
#'doer
))
298 ;; again backwards just for fun
299 (loop for j downfrom
(1- (length worklist
)) to
0 do
300 (info-puthash table
(svref worklist j
) #'doer
))))
301 :name
(format nil
"Worker ~D" i
)
302 :arguments
(list (aref worklists i
) i
))
304 (when print
(format t
"Started ~D threads doing INCF~%" n-threads
))
305 (dolist (thread threads
)
306 (sb-thread:join-thread thread
))
307 (assert (= (info-env-count table
) n-inserts
))
308 (show-tally table tries
"updated" print
)
309 ;; expect val[key] = 2*n-threads for all keys
310 (info-maphash (lambda (k v
)
311 (unless (= v
(* 2 n-threads
))
312 (push (cons k v
) failures
)))
315 (format t
"Fail: ~S~%" failures
))
316 (assert (not failures
))
319 (defun test-concurrent-consing (&key
(table (make-info-hashtable))
320 (n-threads 40) (n-inserts 100000)
321 (randomize t
) (print nil
))
322 (declare (optimize safety
))
323 (assert (evenp n-threads
))
325 (rs (make-random-state)))
326 ;; Under each key, the value stored will be a list of the threads
327 ;; which pushed their ID. For any pair of even/odd numbered threads,
328 ;; exactly one should win the race to push its ID on behalf of the pair.
329 (dotimes (i n-threads
)
330 (push (sb-thread:make-thread
332 ;; Randomizing makes keys be used up in a quasi-random
333 ;; order without having to pre-compute a shuffle.
334 (dotimes (i n-inserts
)
336 table
(randomize (1+ i
) rs
)
338 (let ((peer (logxor me
1)))
339 (if (member peer list
) list
(cons me list
)))))))
340 :name
(format nil
"Worker ~D" i
)
341 :arguments
(list i
(if randomize
(make-random-state rs
))))
343 (when print
(format t
"Started ~D threads doing CONS~%" n-threads
))
344 (dolist (thread threads
)
345 (sb-thread:join-thread thread
))
346 (assert (= (info-env-count table
) n-inserts
))
347 ;; Collect the distribution of threads which inserted, for display only
348 ;; since it not expected to be particularly "fair"
349 (let ((tally (make-array n-threads
:initial-element
0)))
351 (lambda (key id-list
)
352 (let ((scoreboard (make-array (/ n-threads
2) :element-type
'bit
)))
353 (dolist (thread-id id-list
)
354 (let ((group-id (floor thread-id
2)))
355 ;; assert no duplicate for a peer group
356 (if (= (sbit scoreboard group-id
) 1)
357 (error "Fail: ~S ~S~%" key id-list
))
358 (setf (sbit scoreboard group-id
) 1)
359 (incf (aref tally thread-id
))))
360 ;; the scoreboard should be full
361 (when (find 0 scoreboard
)
362 (error "Fail: ~S -> ~S (~S)~%" key id-list scoreboard
))))
364 ;; There should be half as many puthash operations that succeeded
365 ;; as the product of n-threads and n-inserts.
366 (assert (= (show-tally table tally
"inserted" print
)
367 (* 1/2 n-threads n-inserts
)))))
372 (test-util:with-test
(:name
:lockfree-hash-concurrent-twiddling
)
373 (test-concurrent-incf))
374 (test-util:with-test
(:name
:lockfree-hash-concurrent-consing
)
375 (test-concurrent-consing)))
379 (in-package "SB-IMPL")
381 (defglobal *make-classoid-cell-callcount
* (make-array 1 :element-type
'sb-ext
:word
))
382 (defglobal *really-make-classoid-cell
* #'sb-kernel
::make-classoid-cell
)
383 (without-package-locks
384 (defun sb-kernel::make-classoid-cell
(name &optional classoid
)
385 (sb-ext:atomic-incf
(aref *make-classoid-cell-callcount
* 0))
386 (funcall *really-make-classoid-cell
* name classoid
)))
388 ;; Return a set of symbols to play around with
389 (defun classoid-cell-test-get-lotsa-symbols ()
392 (package-hashtable-cells
393 (package-internal-symbols (find-package "SB-C")))))
395 ;; Make every symbol in the test set have a classoid-cell
396 (defun be-a-classoid-cell-writer ()
397 (let* ((symbols (classoid-cell-test-get-lotsa-symbols))
398 (result (make-array (length symbols
) :initial-element nil
)))
399 (loop for s across symbols
401 do
(setf (aref result i
) (find-classoid-cell s
:create t
)))
404 ;; Get the classoid-cells
405 (defun be-a-classoid-cell-reader ()
406 (let* ((symbols (classoid-cell-test-get-lotsa-symbols))
407 (result (make-array (length symbols
) :initial-element nil
)))
409 (loop for i below
(length symbols
)
410 do
(pushnew (find-classoid-cell (svref symbols i
))
412 ;; The thread shall have observed at most two different values
413 ;; for FIND-CLASSOID-CELL - nil and/or a CLASSOID-CELL.
414 ;; For each symbol, if the thread observed a classoid cell, store that.
415 (loop for list across result
417 do
(let ((observed-value (remove nil list
)))
418 (if (cdr observed-value
)
419 (error "Should not happen: find-classoid-cell => ~S" list
)
420 (setf (svref result i
) (car observed-value
)))))
423 ;; Perform some silly updates to plists, because they mess with
424 ;; the symbol-info slot alongside globaldb writers.
425 (defun be-a-plist-writer ()
426 (loop for s across
(classoid-cell-test-get-lotsa-symbols)
428 (loop (let ((old (symbol-plist s
)))
429 (when (or (member 'foo old
)
430 (eq (cas (symbol-plist s
) old
(list* 'foo s old
)) old
))
434 (test-util:with-test
(:name
:info-vector-classoid-cell
)
435 (let (readers writers more-threads results
)
437 (push (sb-thread:make-thread
#'be-a-classoid-cell-writer
) writers
))
439 (push (sb-thread:make-thread
#'be-a-classoid-cell-reader
) readers
)
440 (push (sb-thread:make-thread
#'be-a-plist-writer
) more-threads
))
441 (mapc #'sb-thread
:join-thread more-threads
)
442 (dolist (thread (append readers writers
))
443 (push (sb-thread:join-thread thread
) results
))
444 (let ((result-vect (make-array 10)))
445 (loop for i below
(length (first results
))
447 (dotimes (thread-num 10)
448 (setf (aref result-vect thread-num
)
449 (aref (nth thread-num results
) i
)))
450 ;; some thread should have observed a classoid-cell
451 (let ((representative (find-if-not #'null result-vect
)))
452 ;; For each thread which observed a classoid-cell,
453 ;; assert that the cell is EQ to the representative.
454 (dotimes (thread-num 10)
455 (let ((cell (aref result-vect thread-num
)))
457 (assert (eq cell representative
))))))))
458 ;; and make sure the property list updates also weren't lost
459 (let ((symbols (classoid-cell-test-get-lotsa-symbols)))
460 (loop for s across symbols
461 do
(assert (eq (get s
'foo
) s
)))
462 ;; a statistic of no real merit, but verifies that
463 ;; the lockfree logic does discard some created objects.
464 (format t
"Consed ~D classoid-cells (~D symbols)~%"
465 (aref *make-classoid-cell-callcount
* 0)
468 ;;; test %GET-INFO-VALUE-INITIALIZING using generalized function names
470 (defun be-an-fdefn-reader (names)
471 (declare (simple-vector names
))
472 (let ((result (make-array (length names
) :initial-element nil
)))
474 (loop for i below
(length names
)
475 do
(pushnew (find-fdefn (aref names i
)) (aref result i
))))
476 ;; The thread shall observe either nil or an fdefn, and at most one fdefn.
477 (loop for list across result
479 do
(let ((observed-value (remove nil list
)))
480 (if (cdr observed-value
)
481 (error "Should not happen: fdefn => ~S" list
)
482 (setf (aref result i
) (car observed-value
)))))
485 (defun be-an-fdefn-writer (names)
486 (declare (simple-vector names
))
487 (let ((fdefn-result (make-array (length names
) :initial-element nil
))
488 (random-result (make-array (length names
) :initial-element nil
))
491 (position-if #'identity sb-c
::*info-types
*
492 :end sb-int
:+fdefn-info-num
+ :from-end t
)))
493 (loop for name across names
495 do
(setf (aref fdefn-result i
)
496 (get-info-value-initializing
497 :function
:definition name
498 (progn (incf n-created
) (make-fdefn name
))))
499 (dotimes (i (random 3))
500 ;; Set random info for other names to cause CAS failures.
501 ;; Pick an info-type number and give it a random value.
502 ;; Store the random value so that we can assert on it later.
503 ;; Never touch reserved type numbers 0 or 63.
504 (let ((random-name-index (random (length names
)))
505 (random-type (+ (random (1- highest-type-num
)) 2))
506 (random-value (random most-positive-fixnum
)))
507 (push (cons random-type random-value
)
508 (aref random-result random-name-index
))
509 (sb-c::set-info-value
(aref names random-name-index
)
510 random-type random-value
))))
511 (values n-created fdefn-result random-result
)))
513 (test-util:with-test
(:name
:get-info-value-initializing
514 :skipped-on
'(not :sb-thread
))
515 ;; Precompute random generalized function names for testing, some of which
516 ;; are "simple" (per the taxonomy of globaldb) and some hairy.
517 (let ((work (coerce (loop repeat
10000
518 nconc
(list `(defmacro ,(gensym)) ; simple name
519 (gensym))) ; very simple name
521 (n-threads 10) readers writers fdefn-results random-results
)
522 (dotimes (i (ash n-threads -
1))
523 (push (sb-thread:make-thread
524 #'be-an-fdefn-writer
:arguments
(list work
)
525 :name
(write-to-string i
)) writers
))
526 (dotimes (i (ash n-threads -
1))
527 (push (sb-thread:make-thread
#'be-an-fdefn-reader
:arguments
(list work
))
529 (dolist (thread readers
)
530 (push (sb-thread:join-thread thread
) fdefn-results
))
532 (dolist (thread writers
)
533 (multiple-value-bind (n-created fdefn-result random-result
)
534 (sb-thread:join-thread thread
)
536 (format t
"~5D fdefns from ~A~%" n-created
537 (sb-thread:thread-name thread
))
538 (push fdefn-result fdefn-results
)
539 (push random-result random-results
)))
540 (format t
"~5D total~%" tot
))
541 (let ((aggregate (make-array n-threads
)))
542 (dotimes (name-index (length work
))
543 (dotimes (thread-num n-threads
)
544 (setf (aref aggregate thread-num
)
545 (aref (nth thread-num fdefn-results
) name-index
)))
546 ;; some thread should have observed an fdefn
547 (let ((representative (find-if-not #'null aggregate
)))
548 ;; For each thread which observed an fdefn,
549 ;; assert that the cell is EQ to the representative.
550 (dotimes (thread-num n-threads
)
551 (awhen (aref aggregate thread-num
)
552 (assert (eq it representative
)))))))
553 ;; For each name and each info type number that some thread inserted,
554 ;; verify that the info-value is among the set of random values.
555 (dotimes (name-index (length work
))
556 (dotimes (type-num 64)
557 ;; some thread says that TYPE-NUM exists for NAME-INDEX
558 (when (some (lambda (output)
559 (assoc type-num
(aref output name-index
)))
561 (let ((actual (sb-c::get-info-value
(aref work name-index
)
563 (unless (some (lambda (output)
565 (and (eq (car cell
) type-num
)
566 (eql (cdr cell
) actual
)))
567 (aref output name-index
)))
569 (error "Fail ~S ~S => ~S.~%Choices are ~S"
570 (aref work name-index
) type-num actual
571 (mapcar (lambda (output)
572 (aref output name-index
))
573 random-results
)))))))))
575 ;; As explained in the comments at the top of 'info-vector.lisp',
576 ;; it is a bad idea to use globaldb to store an atomic counter as
577 ;; a piece of info for a name, as it is quite brutal and consy,
578 ;; but for this test, that's precisely the goal.
579 ;; This test conses ~5 Megabytes on 64-bit almost entirely due
580 ;; to allocation of each immutable info storage vector.
581 (test-util:with-test
(:name
:get-info-value-updating
582 :skipped-on
'(not :sb-thread
))
584 (declare (simple-vector names
))
585 (let* ((n (length names
))
586 (counts (make-array n
:element-type
'sb-ext
:word
))
589 (push (sb-thread:make-thread
592 ;; increment (:variable :macro-expansion)
593 ;; for a randomly chosen name. That particular
594 ;; info-type harmlessly accepts any data type.
595 (let* ((index (random n
))
596 (name (aref names index
)))
597 (atomic-incf (aref counts index
))
598 ;; should probably be SB-INT:
599 (sb-c::atomic-set-info-value
600 :variable
:macro-expansion name
602 (if old-p
(1+ old
) 1))))
603 ;; randomly touch an item of info
604 ;; for another (or the same) name.
605 (let* ((index (random n
))
606 (name (aref names index
)))
607 ;; source-location also accepts anything :-(
608 (setf (info :type
:source-location name
) iter
)))))
610 (mapc #'sb-thread
:join-thread threads
)
611 ;; assert that no updates were lost
612 (loop for name across names
613 for count across counts
614 for val
= (info :variable
:macro-expansion name
)
615 do
(assert (eql (or val
0) count
))))))
616 ;; Try it when names are symbols or "simple" 2-list names
617 (run (coerce (loop repeat
50
619 nconc
(list `(setf ,sym
) sym
))
621 ;; For hairy names, the tricky piece is in the rehash algorithm,
622 ;; but there's no way to stress-test that because *INFO-ENVIRONMENT*
623 ;; would have to keep doubling in size. To that end, it would have to begin
624 ;; as a tiny table again, but it can't, without destroying the Lisp runtime.
625 ;; The :lockfree-hash-concurrent-twiddling test should give high confidence
626 ;; that it works, by creating and testing a standalone hash-table.
627 (run (coerce (loop repeat
50 collect
`(foo ,(gensym) hair
)) 'vector
))))