1.0.37.45: Remove bogus test (:CONDITION-VARIABLE :WAIT-MULTIPLE).
[sbcl.git] / tests / threads.impure.lisp
1 ;;;; miscellaneous tests of thread stuff
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
6 ;;;; While most of SBCL is derived from the CMU CL system, the test
7 ;;;; files (like this one) were written from scratch after the fork
8 ;;;; from CMU CL.
9 ;;;
10 ;;;; This software is in the public domain and is provided with
11 ;;;; absoluely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
13
14 ; WHITE-BOX TESTS
15
16 (in-package "SB-THREAD")
17 (use-package :test-util)
18 (use-package "ASSERTOID")
19
20 (setf sb-unix::*on-dangerous-select* :error)
21
22 (defun wait-for-threads (threads)
23   (mapc (lambda (thread) (sb-thread:join-thread thread :default nil)) threads)
24   (assert (not (some #'sb-thread:thread-alive-p threads))))
25
26 (with-test (:name (:threads :trivia))
27   (assert (eql 1 (length (list-all-threads))))
28
29   (assert (eq *current-thread*
30               (find (thread-name *current-thread*) (list-all-threads)
31                     :key #'thread-name :test #'equal)))
32
33   (assert (thread-alive-p *current-thread*)))
34
35 (with-test (:name (:with-mutex :basics))
36   (let ((mutex (make-mutex)))
37     (with-mutex (mutex)
38       mutex)))
39
40 (with-test (:name (:with-spinlock :basics))
41   (let ((spinlock (make-spinlock)))
42     (with-spinlock (spinlock))))
43
44 (sb-alien:define-alien-routine "check_deferrables_blocked_or_lose"
45     void
46   (where sb-alien:unsigned-long))
47 (sb-alien:define-alien-routine "check_deferrables_unblocked_or_lose"
48     void
49   (where sb-alien:unsigned-long))
50
51 (with-test (:name (:interrupt-thread :basics :no-unwinding))
52   (let ((a 0))
53     (interrupt-thread *current-thread* (lambda () (setq a 1)))
54     (assert (eql a 1))))
55
56 (with-test (:name (:interrupt-thread :deferrables-blocked))
57   (sb-thread:interrupt-thread sb-thread:*current-thread*
58                               (lambda ()
59                                 (check-deferrables-blocked-or-lose 0))))
60
61 (with-test (:name (:interrupt-thread :deferrables-unblocked))
62   (sb-thread:interrupt-thread sb-thread:*current-thread*
63                               (lambda ()
64                                 (with-interrupts
65                                   (check-deferrables-unblocked-or-lose 0)))))
66
67 (with-test (:name (:interrupt-thread :nlx))
68   (catch 'xxx
69     (sb-thread:interrupt-thread sb-thread:*current-thread*
70                                 (lambda ()
71                                   (check-deferrables-blocked-or-lose 0)
72                                   (throw 'xxx nil))))
73   (check-deferrables-unblocked-or-lose 0))
74
75 #-sb-thread (sb-ext:quit :unix-status 104)
76
77 ;;;; Now the real tests...
78
79 (with-test (:name (:interrupt-thread :deferrables-unblocked-by-spinlock))
80   (let ((spinlock (sb-thread::make-spinlock))
81         (thread (sb-thread:make-thread (lambda ()
82                                          (loop (sleep 1))))))
83     (sb-thread::get-spinlock spinlock)
84     (sb-thread:interrupt-thread thread
85                                 (lambda ()
86                                   (check-deferrables-blocked-or-lose 0)
87                                   (sb-thread::get-spinlock spinlock)
88                                   (check-deferrables-unblocked-or-lose 0)
89                                   (sb-ext:quit)))
90     (sleep 1)
91     (sb-thread::release-spinlock spinlock)))
92
93 ;;; compare-and-swap
94
95 (defmacro defincf (name accessor &rest args)
96   `(defun ,name (x)
97      (let* ((old (,accessor x ,@args))
98          (new (1+ old)))
99     (loop until (eq old (sb-ext:compare-and-swap (,accessor x ,@args) old new))
100        do (setf old (,accessor x ,@args)
101                 new (1+ old)))
102     new)))
103
104 (defstruct cas-struct (slot 0))
105
106 (defincf incf-car car)
107 (defincf incf-cdr cdr)
108 (defincf incf-slot cas-struct-slot)
109 (defincf incf-symbol-value symbol-value)
110 (defincf incf-svref/1 svref 1)
111 (defincf incf-svref/0 svref 0)
112
113 (defmacro def-test-cas (name init incf op)
114   `(with-test (:name ,name)
115      (flet ((,name (n)
116               (declare (fixnum n))
117               (let* ((x ,init)
118                      (run nil)
119                      (threads
120                       (loop repeat 10
121                             collect (sb-thread:make-thread
122                                      (lambda ()
123                                        (loop until run
124                                              do (sb-thread:thread-yield))
125                                        (loop repeat n do (,incf x)))))))
126                 (setf run t)
127                 (dolist (th threads)
128                   (sb-thread:join-thread th))
129                 (assert (= (,op x) (* 10 n))))))
130        (,name 200000))))
131
132 (def-test-cas test-cas-car (cons 0 nil) incf-car car)
133 (def-test-cas test-cas-cdr (cons nil 0) incf-cdr cdr)
134 (def-test-cas test-cas-slot (make-cas-struct) incf-slot cas-struct-slot)
135 (def-test-cas test-cas-value (let ((x '.x.))
136                                (set x 0)
137                                x)
138   incf-symbol-value symbol-value)
139 (def-test-cas test-cas-svref/0 (vector 0 nil) incf-svref/0 (lambda (x)
140                                                              (svref x 0)))
141 (def-test-cas test-cas-svref/1 (vector nil 0) incf-svref/1 (lambda (x)
142                                                              (svref x 1)))
143 (format t "~&compare-and-swap tests done~%")
144
145 (with-test (:name (:threads :more-trivia)))
146 (let ((old-threads (list-all-threads))
147       (thread (make-thread (lambda ()
148                              (assert (find *current-thread* *all-threads*))
149                              (sleep 2))))
150       (new-threads (list-all-threads)))
151   (assert (thread-alive-p thread))
152   (assert (eq thread (first new-threads)))
153   (assert (= (1+ (length old-threads)) (length new-threads)))
154   (sleep 3)
155   (assert (not (thread-alive-p thread))))
156
157 (with-test (:name '(:join-thread :nlx :default))
158   (let ((sym (gensym)))
159     (assert (eq sym (join-thread (make-thread (lambda () (sb-ext:quit)))
160                                  :default sym)))))
161
162 (with-test (:name '(:join-thread :nlx :error))
163   (raises-error? (join-thread (make-thread (lambda () (sb-ext:quit))))
164                  join-thread-error))
165
166 (with-test (:name '(:join-thread :multiple-values))
167   (assert (equal '(1 2 3)
168                  (multiple-value-list
169                   (join-thread (make-thread (lambda () (values 1 2 3))))))))
170
171 ;;; We had appalling scaling properties for a while.  Make sure they
172 ;;; don't reappear.
173 (defun scaling-test (function &optional (nthreads 5))
174   "Execute FUNCTION with NTHREADS lurking to slow it down."
175   (let ((queue (sb-thread:make-waitqueue))
176         (mutex (sb-thread:make-mutex)))
177     ;; Start NTHREADS idle threads.
178     (dotimes (i nthreads)
179       (sb-thread:make-thread (lambda ()
180                                (with-mutex (mutex)
181                                  (sb-thread:condition-wait queue mutex))
182                                (sb-ext:quit))))
183     (let ((start-time (get-internal-run-time)))
184       (funcall function)
185       (prog1 (- (get-internal-run-time) start-time)
186         (sb-thread:condition-broadcast queue)))))
187 (defun fact (n)
188   "A function that does work with the CPU."
189   (if (zerop n) 1 (* n (fact (1- n)))))
190 (let ((work (lambda () (fact 15000))))
191   (let ((zero (scaling-test work 0))
192         (four (scaling-test work 4)))
193     ;; a slightly weak assertion, but good enough for starters.
194     (assert (< four (* 1.5 zero)))))
195
196 ;;; For one of the interupt-thread tests, we want a foreign function
197 ;;; that does not make syscalls
198
199 (with-open-file (o "threads-foreign.c" :direction :output :if-exists :supersede)
200   (format o "void loop_forever() { while(1) ; }~%"))
201 (sb-ext:run-program
202  #-sunos "cc" #+sunos "gcc"
203  (or #+(or linux freebsd sunos) '(#+x86-64 "-fPIC"
204                                   "-shared" "-o" "threads-foreign.so" "threads-foreign.c")
205      #+darwin '(#+x86-64 "-arch" #+x86-64 "x86_64"
206                 "-dynamiclib" "-o" "threads-foreign.so" "threads-foreign.c")
207      (error "Missing shared library compilation options for this platform"))
208  :search t)
209 (sb-alien:load-shared-object (truename "threads-foreign.so"))
210 (sb-alien:define-alien-routine loop-forever sb-alien:void)
211 (delete-file "threads-foreign.c")
212
213
214 ;;; elementary "can we get a lock and release it again"
215 (with-test (:name (:mutex :basics))
216   (let ((l (make-mutex :name "foo"))
217         (p *current-thread*))
218     (assert (eql (mutex-value l) nil) nil "1")
219     (sb-thread:get-mutex l)
220     (assert (eql (mutex-value l) p) nil "3")
221     (sb-thread:release-mutex l)
222     (assert (eql (mutex-value l) nil) nil "5")))
223
224 (with-test (:name (:with-recursive-lock :basics))
225   (labels ((ours-p (value)
226              (eq *current-thread* value)))
227     (let ((l (make-mutex :name "rec")))
228       (assert (eql (mutex-value l) nil) nil "1")
229       (sb-thread:with-recursive-lock (l)
230         (assert (ours-p (mutex-value l)) nil "3")
231         (sb-thread:with-recursive-lock (l)
232           (assert (ours-p (mutex-value l)) nil "4"))
233         (assert (ours-p (mutex-value l)) nil "5"))
234       (assert (eql (mutex-value l) nil) nil "6"))))
235
236 (with-test (:name (:with-recursive-spinlock :basics))
237   (labels ((ours-p (value)
238              (eq *current-thread* value)))
239     (let ((l (make-spinlock :name "rec")))
240       (assert (eql (spinlock-value l) nil) nil "1")
241       (with-recursive-spinlock (l)
242         (assert (ours-p (spinlock-value l)) nil "3")
243         (with-recursive-spinlock (l)
244           (assert (ours-p (spinlock-value l)) nil "4"))
245         (assert (ours-p (spinlock-value l)) nil "5"))
246       (assert (eql (spinlock-value l) nil) nil "6"))))
247
248 (with-test (:name (:mutex :nesting-mutex-and-recursive-lock))
249   (let ((l (make-mutex :name "a mutex")))
250     (with-mutex (l)
251       (with-recursive-lock (l)))))
252
253 (with-test (:name (:spinlock :nesting-spinlock-and-recursive-spinlock))
254   (let ((l (make-spinlock :name "a spinlock")))
255     (with-spinlock (l)
256       (with-recursive-spinlock (l)))))
257
258 (with-test (:name (:spinlock :more-basics))
259   (let ((l (make-spinlock :name "spinlock")))
260     (assert (eql (spinlock-value l) nil) ((spinlock-value l))
261             "spinlock not free (1)")
262     (with-spinlock (l)
263       (assert (eql (spinlock-value l) *current-thread*) ((spinlock-value l))
264               "spinlock not taken"))
265     (assert (eql (spinlock-value l) nil) ((spinlock-value l))
266             "spinlock not free (2)")))
267
268 ;; test that SLEEP actually sleeps for at least the given time, even
269 ;; if interrupted by another thread exiting/a gc/anything
270 (with-test (:name (:sleep :continue-sleeping-after-interrupt))
271   (let ((start-time (get-universal-time)))
272     (make-thread (lambda () (sleep 1) (sb-ext:gc :full t)))
273     (sleep 5)
274     (assert (>= (get-universal-time) (+ 5 start-time)))))
275
276
277 (with-test (:name (:condition-wait :basics-1))
278   (let ((queue (make-waitqueue :name "queue"))
279         (lock (make-mutex :name "lock"))
280         (n 0))
281     (labels ((in-new-thread ()
282                (with-mutex (lock)
283                  (assert (eql (mutex-value lock) *current-thread*))
284                  (format t "~A got mutex~%" *current-thread*)
285                  ;; now drop it and sleep
286                  (condition-wait queue lock)
287                  ;; after waking we should have the lock again
288                  (assert (eql (mutex-value lock) *current-thread*))
289                  (assert (eql n 1))
290                  (decf n))))
291       (make-thread #'in-new-thread)
292       (sleep 2)            ; give it  a chance to start
293       ;; check the lock is free while it's asleep
294       (format t "parent thread ~A~%" *current-thread*)
295       (assert (eql (mutex-value lock) nil))
296       (with-mutex (lock)
297         (incf n)
298         (condition-notify queue))
299       (sleep 1))))
300
301 (with-test (:name (:condition-wait :basics-2))
302   (let ((queue (make-waitqueue :name "queue"))
303         (lock (make-mutex :name "lock")))
304     (labels ((ours-p (value)
305                (eq *current-thread* value))
306              (in-new-thread ()
307                (with-recursive-lock (lock)
308                  (assert (ours-p (mutex-value lock)))
309                  (format t "~A got mutex~%" (mutex-value lock))
310                  ;; now drop it and sleep
311                  (condition-wait queue lock)
312                  ;; after waking we should have the lock again
313                  (format t "woken, ~A got mutex~%" (mutex-value lock))
314                  (assert (ours-p (mutex-value lock))))))
315       (make-thread #'in-new-thread)
316       (sleep 2)            ; give it  a chance to start
317       ;; check the lock is free while it's asleep
318       (format t "parent thread ~A~%" *current-thread*)
319       (assert (eql (mutex-value lock) nil))
320       (with-recursive-lock (lock)
321         (condition-notify queue))
322       (sleep 1))))
323
324 (with-test (:name (:mutex :contention))
325   (let ((mutex (make-mutex :name "contended")))
326     (labels ((run ()
327                (let ((me *current-thread*))
328                  (dotimes (i 100)
329                    (with-mutex (mutex)
330                      (sleep .03)
331                      (assert (eql (mutex-value mutex) me)))
332                    (assert (not (eql (mutex-value mutex) me))))
333                  (format t "done ~A~%" *current-thread*))))
334       (let ((kid1 (make-thread #'run))
335             (kid2 (make-thread #'run)))
336         (format t "contention ~A ~A~%" kid1 kid2)
337         (wait-for-threads (list kid1 kid2))))))
338
339 ;;; GRAB-MUTEX
340
341 (with-test (:name (:grab-mutex :waitp nil))
342   (let ((m (make-mutex)))
343     (with-mutex (m)
344       (assert (null (join-thread (make-thread
345                                   #'(lambda ()
346                                       (grab-mutex m :waitp nil)))))))))
347
348 (with-test (:name (:grab-mutex :timeout :acquisition-fail))
349   (let ((m (make-mutex)))
350     (with-mutex (m)
351       (assert (null (join-thread (make-thread
352                                   #'(lambda ()
353                                       (grab-mutex m :timeout 0.1)))))))))
354
355 (with-test (:name (:grab-mutex :timeout :acquisition-success))
356   (let ((m (make-mutex))
357         (child))
358     (with-mutex (m)
359       (setq child (make-thread #'(lambda () (grab-mutex m :timeout 1.0))))
360       (sleep 0.2))
361     (assert (eq (join-thread child) 't))))
362
363 (with-test (:name (:grab-mutex :timeout+deadline))
364   (let ((m (make-mutex)))
365     (with-mutex (m)
366       (assert (eq (join-thread
367                    (make-thread #'(lambda ()
368                                     (sb-sys:with-deadline (:seconds 0.0)
369                                       (handler-case
370                                           (grab-mutex m :timeout 0.0)
371                                         (sb-sys:deadline-timeout ()
372                                           :deadline))))))
373                   :deadline)))))
374
375 (with-test (:name (:grab-mutex :waitp+deadline))
376   (let ((m (make-mutex)))
377     (with-mutex (m)
378       (assert (eq (join-thread
379                    (make-thread #'(lambda ()
380                                     (sb-sys:with-deadline (:seconds 0.0)
381                                       (handler-case
382                                           (grab-mutex m :waitp nil)
383                                         (sb-sys:deadline-timeout ()
384                                           :deadline))))))
385                   'nil)))))
386
387 ;;; semaphores
388
389 (defmacro raises-timeout-p (&body body)
390   `(handler-case (progn (progn ,@body) nil)
391     (sb-ext:timeout () t)))
392
393 (with-test (:name (:semaphore :wait-forever))
394   (let ((sem (make-semaphore :count 0)))
395     (assert (raises-timeout-p
396               (sb-ext:with-timeout 0.1
397                 (wait-on-semaphore sem))))))
398
399 (with-test (:name (:semaphore :initial-count))
400   (let ((sem (make-semaphore :count 1)))
401     (sb-ext:with-timeout 0.1
402       (wait-on-semaphore sem))))
403
404 (with-test (:name (:semaphore :wait-then-signal))
405   (let ((sem (make-semaphore))
406         (signalled-p nil))
407     (make-thread (lambda ()
408                    (sleep 0.1)
409                    (setq signalled-p t)
410                    (signal-semaphore sem)))
411     (wait-on-semaphore sem)
412     (assert signalled-p)))
413
414 (with-test (:name (:semaphore :signal-then-wait))
415   (let ((sem (make-semaphore))
416         (signalled-p nil))
417     (make-thread (lambda ()
418                    (signal-semaphore sem)
419                    (setq signalled-p t)))
420     (loop until signalled-p)
421     (wait-on-semaphore sem)
422     (assert signalled-p)))
423
424 (defun test-semaphore-multiple-signals (wait-on-semaphore)
425   (let* ((sem (make-semaphore :count 5))
426          (threads (loop repeat 20 collecting
427                         (make-thread (lambda ()
428                                        (funcall wait-on-semaphore sem))))))
429     (flet ((count-live-threads ()
430              (count-if #'thread-alive-p threads)))
431       (sleep 0.5)
432       (assert (= 15 (count-live-threads)))
433       (signal-semaphore sem 10)
434       (sleep 0.5)
435       (assert (= 5 (count-live-threads)))
436       (signal-semaphore sem 3)
437       (sleep 0.5)
438       (assert (= 2 (count-live-threads)))
439       (signal-semaphore sem 4)
440       (sleep 0.5)
441       (assert (= 0 (count-live-threads))))))
442
443 (with-test (:name (:semaphore :multiple-signals))
444   (test-semaphore-multiple-signals #'wait-on-semaphore))
445
446 (with-test (:name (:try-semaphore :trivial-fail))
447   (assert (eq (try-semaphore (make-semaphore :count 0)) 'nil)))
448
449 (with-test (:name (:try-semaphore :trivial-success))
450   (let ((sem (make-semaphore :count 1)))
451     (assert (try-semaphore sem))
452     (assert (zerop (semaphore-count sem)))))
453
454 (with-test (:name (:try-semaphore :trivial-fail :n>1))
455   (assert (eq (try-semaphore (make-semaphore :count 1) 2) 'nil)))
456
457 (with-test (:name (:try-semaphore :trivial-success :n>1))
458   (let ((sem (make-semaphore :count 10)))
459     (assert (try-semaphore sem 5))
460     (assert (try-semaphore sem 5))
461     (assert (zerop (semaphore-count sem)))))
462
463 (with-test (:name (:try-semaphore :emulate-wait-on-semaphore))
464   (flet ((busy-wait-on-semaphore (sem)
465            (loop until (try-semaphore sem) do (sleep 0.001))))
466     (test-semaphore-multiple-signals #'busy-wait-on-semaphore)))
467
468 ;;; Here we test that interrupting TRY-SEMAPHORE does not leave a
469 ;;; semaphore in a bad state.
470 (with-test (:name (:try-semaphore :interrupt-safe))
471   (flet ((make-threads (count fn)
472            (loop repeat count collect (make-thread fn)))
473          (kill-thread (thread)
474            (when (thread-alive-p thread)
475              (ignore-errors (terminate-thread thread))))
476          (count-live-threads (threads)
477            (count-if #'thread-alive-p threads)))
478     ;; WAITERS will already be waiting on the semaphore while
479     ;; threads-being-interrupted will perform TRY-SEMAPHORE on that
480     ;; semaphore, and MORE-WAITERS are new threads trying to wait on
481     ;; the semaphore during the interruption-fire.
482     (let* ((sem (make-semaphore :count 100))
483            (waiters (make-threads 20 #'(lambda ()
484                                          (wait-on-semaphore sem))))
485            (triers  (make-threads 40 #'(lambda ()
486                                          (sleep (random 0.01))
487                                          (try-semaphore sem (1+ (random 5))))))
488            (more-waiters
489             (loop repeat 10
490                   do (kill-thread (nth (random 40) triers))
491                   collect (make-thread #'(lambda () (wait-on-semaphore sem)))
492                   do (kill-thread (nth (random 40) triers)))))
493       (sleep 0.5)
494       ;; Now ensure that the waiting threads will all be waked up,
495       ;; i.e. that the semaphore is still working.
496       (loop repeat (+ (count-live-threads waiters)
497                       (count-live-threads more-waiters))
498             do (signal-semaphore sem))
499       (sleep 0.5)
500       (assert (zerop (count-live-threads triers)))
501       (assert (zerop (count-live-threads waiters)))
502       (assert (zerop (count-live-threads more-waiters))))))
503
504
505
506 (format t "~&semaphore tests done~%")
507
508 (defun test-interrupt (function-to-interrupt &optional quit-p)
509   (let ((child  (make-thread function-to-interrupt)))
510     ;;(format t "gdb ./src/runtime/sbcl ~A~%attach ~A~%" child child)
511     (sleep 2)
512     (format t "interrupting child ~A~%" child)
513     (interrupt-thread child
514                       (lambda ()
515                         (format t "child pid ~A~%" *current-thread*)
516                         (when quit-p (sb-ext:quit))))
517     (sleep 1)
518     child))
519
520 ;; separate tests for (a) interrupting Lisp code, (b) C code, (c) a syscall,
521 ;; (d) waiting on a lock, (e) some code which we hope is likely to be
522 ;; in pseudo-atomic
523
524 (with-test (:name (:interrupt-thread :more-basics))
525   (let ((child (test-interrupt (lambda () (loop)))))
526     (terminate-thread child)))
527
528 (with-test (:name (:interrupt-thread :interrupt-foreign-loop))
529   (test-interrupt #'loop-forever :quit))
530
531 (with-test (:name (:interrupt-thread :interrupt-sleep))
532   (let ((child (test-interrupt (lambda () (loop (sleep 2000))))))
533     (terminate-thread child)
534     (wait-for-threads (list child))))
535
536 (with-test (:name (:interrupt-thread :interrupt-mutex-acquisition))
537   (let ((lock (make-mutex :name "loctite"))
538         child)
539     (with-mutex (lock)
540       (setf child (test-interrupt
541                    (lambda ()
542                      (with-mutex (lock)
543                        (assert (eql (mutex-value lock) *current-thread*)))
544                      (assert (not (eql (mutex-value lock) *current-thread*)))
545                      (sleep 10))))
546       ;;hold onto lock for long enough that child can't get it immediately
547       (sleep 5)
548       (interrupt-thread child (lambda () (format t "l ~A~%" (mutex-value lock))))
549       (format t "parent releasing lock~%"))
550     (terminate-thread child)
551     (wait-for-threads (list child))))
552
553 (format t "~&locking test done~%")
554
555 (defun alloc-stuff () (copy-list '(1 2 3 4 5)))
556
557 (with-test (:name (:interrupt-thread :interrupt-consing-child))
558   (let ((thread (sb-thread:make-thread (lambda () (loop (alloc-stuff))))))
559     (let ((killers
560            (loop repeat 4 collect
561                  (sb-thread:make-thread
562                   (lambda ()
563                     (loop repeat 25 do
564                           (sleep (random 0.1d0))
565                           (princ ".")
566                           (force-output)
567                           (sb-thread:interrupt-thread thread (lambda ()))))))))
568       (wait-for-threads killers)
569       (sb-thread:terminate-thread thread)
570       (wait-for-threads (list thread))))
571   (sb-ext:gc :full t))
572
573 (format t "~&multi interrupt test done~%")
574
575 (with-test (:name (:interrupt-thread :interrupt-consing-child :again))
576   (let ((c (make-thread (lambda () (loop (alloc-stuff))))))
577     ;; NB this only works on x86: other ports don't have a symbol for
578     ;; pseudo-atomic atomicity
579     (dotimes (i 100)
580       (sleep (random 0.1d0))
581       (interrupt-thread c
582                         (lambda ()
583                           (princ ".") (force-output)
584                           (assert (thread-alive-p *current-thread*))
585                           (assert
586                            (not (logbitp 0 SB-KERNEL:*PSEUDO-ATOMIC-BITS*))))))
587     (terminate-thread c)
588     (wait-for-threads (list c))))
589
590 (format t "~&interrupt test done~%")
591
592 (defstruct counter (n 0 :type sb-vm:word))
593 (defvar *interrupt-counter* (make-counter))
594
595 (declaim (notinline check-interrupt-count))
596 (defun check-interrupt-count (i)
597   (declare (optimize (debug 1) (speed 1)))
598   ;; This used to lose if eflags were not restored after an interrupt.
599   (unless (typep i 'fixnum)
600     (error "!!!!!!!!!!!")))
601
602 (with-test (:name (:interrupt-thread :interrupt-ATOMIC-INCF))
603   (let ((c (make-thread
604             (lambda ()
605               (handler-bind ((error #'(lambda (cond)
606                                         (princ cond)
607                                         (sb-debug:backtrace
608                                          most-positive-fixnum))))
609                 (loop (check-interrupt-count
610                        (counter-n *interrupt-counter*))))))))
611     (let ((func (lambda ()
612                   (princ ".")
613                   (force-output)
614                   (sb-ext:atomic-incf (counter-n *interrupt-counter*)))))
615       (setf (counter-n *interrupt-counter*) 0)
616       (dotimes (i 100)
617         (sleep (random 0.1d0))
618         (interrupt-thread c func))
619       (loop until (= (counter-n *interrupt-counter*) 100) do (sleep 0.1))
620       (terminate-thread c)
621       (wait-for-threads (list c)))))
622
623 (format t "~&interrupt count test done~%")
624
625 (defvar *runningp* nil)
626
627 (with-test (:name (:interrupt-thread :no-nesting))
628   (let ((thread (sb-thread:make-thread
629                  (lambda ()
630                    (catch 'xxx
631                      (loop))))))
632     (declare (special runningp))
633     (sleep 0.2)
634     (sb-thread:interrupt-thread thread
635                                 (lambda ()
636                                     (let ((*runningp* t))
637                                       (sleep 1))))
638     (sleep 0.2)
639     (sb-thread:interrupt-thread thread
640                                 (lambda ()
641                                   (throw 'xxx *runningp*)))
642     (assert (not (sb-thread:join-thread thread)))))
643
644 (with-test (:name (:interrupt-thread :nesting))
645   (let ((thread (sb-thread:make-thread
646                  (lambda ()
647                    (catch 'xxx
648                      (loop))))))
649     (declare (special runningp))
650     (sleep 0.2)
651     (sb-thread:interrupt-thread thread
652                                 (lambda ()
653                                   (let ((*runningp* t))
654                                     (sb-sys:with-interrupts
655                                       (sleep 1)))))
656     (sleep 0.2)
657     (sb-thread:interrupt-thread thread
658                                 (lambda ()
659                                   (throw 'xxx *runningp*)))
660     (assert (sb-thread:join-thread thread))))
661
662 (with-test (:name (:two-threads-running-gc))
663   (let (a-done b-done)
664     (make-thread (lambda ()
665                    (dotimes (i 100)
666                      (sb-ext:gc) (princ "\\") (force-output))
667                    (setf a-done t)))
668     (make-thread (lambda ()
669                    (dotimes (i 25)
670                      (sb-ext:gc :full t)
671                      (princ "/") (force-output))
672                    (setf b-done t)))
673     (loop
674       (when (and a-done b-done) (return))
675       (sleep 1))))
676
677 (terpri)
678
679 (defun waste (&optional (n 100000))
680   (loop repeat n do (make-string 16384)))
681
682 (with-test (:name (:one-thread-runs-gc-while-other-conses))
683   (loop for i below 100 do
684         (princ "!")
685         (force-output)
686         (sb-thread:make-thread
687          #'(lambda ()
688              (waste)))
689         (waste)
690         (sb-ext:gc)))
691
692 (terpri)
693
694 (defparameter *aaa* nil)
695 (with-test (:name (:one-thread-runs-gc-while-other-conses :again))
696   (loop for i below 100 do
697         (princ "!")
698         (force-output)
699         (sb-thread:make-thread
700          #'(lambda ()
701              (let ((*aaa* (waste)))
702                (waste))))
703         (let ((*aaa* (waste)))
704           (waste))
705         (sb-ext:gc)))
706
707 (format t "~&gc test done~%")
708
709 ;; this used to deadlock on session-lock
710 (with-test (:name (:no-session-deadlock))
711   (sb-thread:make-thread (lambda () (sb-ext:gc))))
712
713 (defun exercise-syscall (fn reference-errno)
714   (sb-thread:make-thread
715    (lambda ()
716      (loop do
717           (funcall fn)
718           (let ((errno (sb-unix::get-errno)))
719             (sleep (random 0.1d0))
720             (unless (eql errno reference-errno)
721               (format t "Got errno: ~A (~A) instead of ~A~%"
722                       errno
723                       (sb-unix::strerror)
724                       reference-errno)
725               (force-output)
726               (sb-ext:quit :unix-status 1)))))))
727
728 ;; (nanosleep -1 0) does not fail on FreeBSD
729 (with-test (:name (:exercising-concurrent-syscalls))
730   (let* (#-freebsd
731          (nanosleep-errno (progn
732                             (sb-unix:nanosleep -1 0)
733                             (sb-unix::get-errno)))
734          (open-errno (progn
735                        (open "no-such-file"
736                              :if-does-not-exist nil)
737                        (sb-unix::get-errno)))
738          (threads
739           (list
740            #-freebsd
741            (exercise-syscall (lambda () (sb-unix:nanosleep -1 0)) nanosleep-errno)
742            (exercise-syscall (lambda () (open "no-such-file"
743                                               :if-does-not-exist nil))
744                              open-errno)
745            (sb-thread:make-thread (lambda () (loop (sb-ext:gc) (sleep 1)))))))
746     (sleep 10)
747     (princ "terminating threads")
748     (dolist (thread threads)
749       (sb-thread:terminate-thread thread))))
750
751 (format t "~&errno test done~%")
752
753 (with-test (:name (:terminate-thread-restart))
754   (loop repeat 100 do
755         (let ((thread (sb-thread:make-thread (lambda () (sleep 0.1)))))
756           (sb-thread:interrupt-thread
757            thread
758            (lambda ()
759              (assert (find-restart 'sb-thread:terminate-thread)))))))
760
761 (sb-ext:gc :full t)
762
763 (format t "~&thread startup sigmask test done~%")
764
765 (with-test (:name (:debugger-no-hang-on-session-lock-if-interrupted))
766   (sb-debug::enable-debugger)
767   (let* ((main-thread *current-thread*)
768          (interruptor-thread
769           (make-thread (lambda ()
770                          (sleep 2)
771                          (interrupt-thread main-thread
772                                            (lambda ()
773                                              (with-interrupts
774                                                (break))))
775                          (sleep 2)
776                          (interrupt-thread main-thread #'continue))
777                        :name "interruptor")))
778     (with-session-lock (*session*)
779       (sleep 3))
780     (loop while (thread-alive-p interruptor-thread))))
781
782 (format t "~&session lock test done~%")
783
784 ;; expose thread creation races by exiting quickly
785 (with-test (:name (:no-thread-creation-race :light))
786   (sb-thread:make-thread (lambda ())))
787
788 (with-test (:name (:no-thread-creation-race :heavy))
789   (loop repeat 20 do
790         (wait-for-threads
791          (loop for i below 100 collect
792                (sb-thread:make-thread (lambda ()))))))
793
794 (format t "~&creation test done~%")
795
796 ;; interrupt handlers are per-thread with pthreads, make sure the
797 ;; handler installed in one thread is global
798 (with-test (:name (:global-interrupt-handler))
799   (sb-thread:make-thread
800    (lambda ()
801      (sb-ext:run-program "sleep" '("1") :search t :wait nil))))
802
803 ;;;; Binding stack safety
804
805 (defparameter *x* nil)
806 (defparameter *n-gcs-requested* 0)
807 (defparameter *n-gcs-done* 0)
808
809 (let ((counter 0))
810   (defun make-something-big ()
811     (let ((x (make-string 32000)))
812       (incf counter)
813       (let ((counter counter))
814         (sb-ext:finalize x (lambda () (format t " ~S" counter)
815                                    (force-output)))))))
816
817 (defmacro wait-for-gc ()
818   `(progn
819      (incf *n-gcs-requested*)
820      (loop while (< *n-gcs-done* *n-gcs-requested*))))
821
822 (defun send-gc ()
823   (loop until (< *n-gcs-done* *n-gcs-requested*))
824   (format t "G")
825   (force-output)
826   (sb-ext:gc)
827   (incf *n-gcs-done*))
828
829 (defun exercise-binding ()
830   (loop
831    (let ((*x* (make-something-big)))
832      (let ((*x* 42))
833        ;; at this point the binding stack looks like this:
834        ;; NO-TLS-VALUE-MARKER, *x*, SOMETHING, *x*
835        t))
836    (wait-for-gc)
837    ;; sig_stop_for_gc_handler binds FREE_INTERRUPT_CONTEXT_INDEX. By
838    ;; now SOMETHING is gc'ed and the binding stack looks like this: 0,
839    ;; 0, SOMETHING, 0 (because the symbol slots are zeroed on
840    ;; unbinding but values are not).
841    (let ((*x* nil))
842      ;; bump bsp as if a BIND had just started
843      (incf sb-vm::*binding-stack-pointer* 2)
844      (wait-for-gc)
845      (decf sb-vm::*binding-stack-pointer* 2))))
846
847 (with-test (:name (:binding-stack-gc-safety))
848   (let (threads)
849     (unwind-protect
850          (progn
851            (push (sb-thread:make-thread #'exercise-binding) threads)
852            (push (sb-thread:make-thread (lambda ()
853                                           (loop
854                                            (sleep 0.1)
855                                            (send-gc))))
856                  threads)
857            (sleep 4))
858       (mapc #'sb-thread:terminate-thread threads))))
859
860 (format t "~&binding test done~%")
861
862 ;;; HASH TABLES
863
864 (defvar *errors* nil)
865
866 (defun oops (e)
867   (setf *errors* e)
868   (format t "~&oops: ~A in ~S~%" e *current-thread*)
869   (sb-debug:backtrace)
870   (catch 'done))
871
872 (with-test (:name (:unsynchronized-hash-table))
873   ;; We expect a (probable) error here: parellel readers and writers
874   ;; on a hash-table are not expected to work -- but we also don't
875   ;; expect this to corrupt the image.
876   (let* ((hash (make-hash-table))
877          (*errors* nil)
878          (threads (list (sb-thread:make-thread
879                          (lambda ()
880                            (catch 'done
881                              (handler-bind ((serious-condition 'oops))
882                                (loop
883                                  ;;(princ "1") (force-output)
884                                  (setf (gethash (random 100) hash) 'h)))))
885                          :name "writer")
886                         (sb-thread:make-thread
887                          (lambda ()
888                            (catch 'done
889                              (handler-bind ((serious-condition 'oops))
890                                (loop
891                                  ;;(princ "2") (force-output)
892                                  (remhash (random 100) hash)))))
893                          :name "reader")
894                         (sb-thread:make-thread
895                          (lambda ()
896                            (catch 'done
897                              (handler-bind ((serious-condition 'oops))
898                                (loop
899                                  (sleep (random 1.0))
900                                  (sb-ext:gc :full t)))))
901                          :name "collector"))))
902     (unwind-protect
903          (sleep 10)
904       (mapc #'sb-thread:terminate-thread threads))))
905
906 (format t "~&unsynchronized hash table test done~%")
907
908 (with-test (:name (:synchronized-hash-table))
909   (let* ((hash (make-hash-table :synchronized t))
910          (*errors* nil)
911          (threads (list (sb-thread:make-thread
912                          (lambda ()
913                            (catch 'done
914                              (handler-bind ((serious-condition 'oops))
915                                (loop
916                                  ;;(princ "1") (force-output)
917                                  (setf (gethash (random 100) hash) 'h)))))
918                          :name "writer")
919                         (sb-thread:make-thread
920                          (lambda ()
921                            (catch 'done
922                              (handler-bind ((serious-condition 'oops))
923                                (loop
924                                  ;;(princ "2") (force-output)
925                                  (remhash (random 100) hash)))))
926                          :name "reader")
927                         (sb-thread:make-thread
928                          (lambda ()
929                            (catch 'done
930                              (handler-bind ((serious-condition 'oops))
931                                (loop
932                                  (sleep (random 1.0))
933                                  (sb-ext:gc :full t)))))
934                          :name "collector"))))
935     (unwind-protect
936          (sleep 10)
937       (mapc #'sb-thread:terminate-thread threads))
938     (assert (not *errors*))))
939
940 (format t "~&synchronized hash table test done~%")
941
942 (with-test (:name (:hash-table-parallel-readers))
943   (let ((hash (make-hash-table))
944         (*errors* nil))
945     (loop repeat 50
946           do (setf (gethash (random 100) hash) 'xxx))
947     (let ((threads (list (sb-thread:make-thread
948                           (lambda ()
949                             (catch 'done
950                               (handler-bind ((serious-condition 'oops))
951                                 (loop
952                                       until (eq t (gethash (random 100) hash))))))
953                           :name "reader 1")
954                          (sb-thread:make-thread
955                           (lambda ()
956                             (catch 'done
957                               (handler-bind ((serious-condition 'oops))
958                                 (loop
959                                       until (eq t (gethash (random 100) hash))))))
960                           :name "reader 2")
961                          (sb-thread:make-thread
962                           (lambda ()
963                             (catch 'done
964                               (handler-bind ((serious-condition 'oops))
965                                 (loop
966                                       until (eq t (gethash (random 100) hash))))))
967                           :name "reader 3")
968                          (sb-thread:make-thread
969                           (lambda ()
970                             (catch 'done
971                               (handler-bind ((serious-condition 'oops))
972                                (loop
973                                  (sleep (random 1.0))
974                                  (sb-ext:gc :full t)))))
975                           :name "collector"))))
976       (unwind-protect
977            (sleep 10)
978         (mapc #'sb-thread:terminate-thread threads))
979       (assert (not *errors*)))))
980
981 (format t "~&multiple reader hash table test done~%")
982
983 (with-test (:name (:hash-table-single-accessor-parallel-gc))
984   (let ((hash (make-hash-table))
985         (*errors* nil))
986     (let ((threads (list (sb-thread:make-thread
987                           (lambda ()
988                             (handler-bind ((serious-condition 'oops))
989                               (loop
990                                 (let ((n (random 100)))
991                                   (if (gethash n hash)
992                                       (remhash n hash)
993                                       (setf (gethash n hash) 'h))))))
994                           :name "accessor")
995                          (sb-thread:make-thread
996                           (lambda ()
997                             (handler-bind ((serious-condition 'oops))
998                               (loop
999                                 (sleep (random 1.0))
1000                                 (sb-ext:gc :full t))))
1001                           :name "collector"))))
1002       (unwind-protect
1003            (sleep 10)
1004         (mapc #'sb-thread:terminate-thread threads))
1005       (assert (not *errors*)))))
1006
1007 (format t "~&single accessor hash table test~%")
1008
1009 #|  ;; a cll post from eric marsden
1010 | (defun crash ()
1011 |   (setq *debugger-hook*
1012 |         (lambda (condition old-debugger-hook)
1013 |           (debug:backtrace 10)
1014 |           (unix:unix-exit 2)))
1015 |   #+live-dangerously
1016 |   (mp::start-sigalrm-yield)
1017 |   (flet ((roomy () (loop (with-output-to-string (*standard-output*) (room)))))
1018 |     (mp:make-process #'roomy)
1019 |     (mp:make-process #'roomy)))
1020 |#
1021
1022 (with-test (:name (:condition-variable :notify-multiple))
1023   (flet ((tester (notify-fun)
1024            (let ((queue (make-waitqueue :name "queue"))
1025                  (lock (make-mutex :name "lock"))
1026                  (data nil))
1027              (labels ((test (x)
1028                         (loop
1029                            (with-mutex (lock)
1030                              (format t "condition-wait ~a~%" x)
1031                              (force-output)
1032                              (condition-wait queue lock)
1033                              (format t "woke up ~a~%" x)
1034                              (force-output)
1035                              (push x data)))))
1036                (let ((threads (loop for x from 1 to 10
1037                                     collect
1038                                     (let ((x x))
1039                                       (sb-thread:make-thread (lambda ()
1040                                                                (test x)))))))
1041                  (sleep 5)
1042                  (with-mutex (lock)
1043                    (funcall notify-fun queue))
1044                  (sleep 5)
1045                  (mapcar #'terminate-thread threads)
1046                  ;; Check that all threads woke up at least once
1047                  (assert (= (length (remove-duplicates data)) 10)))))))
1048     (tester (lambda (queue)
1049               (format t "~&(condition-notify queue 10)~%")
1050               (force-output)
1051               (condition-notify queue 10)))
1052     (tester (lambda (queue)
1053               (format t "~&(condition-broadcast queue)~%")
1054               (force-output)
1055               (condition-broadcast queue)))))
1056
1057 (format t "waitqueue wakeup tests done~%")
1058
1059 ;;; Make sure that a deadline handler is not invoked twice in a row in
1060 ;;; CONDITION-WAIT. See LP #512914 for a detailed explanation.
1061 ;;;
1062 #-sb-lutex    ; See KLUDGE above: no deadlines for condition-wait+lutexes.
1063 (with-test (:name (:condition-wait :deadlines :LP-512914))
1064   (let ((n 2) ; was empirically enough to trigger the bug
1065         (mutex (sb-thread:make-mutex))
1066         (waitq (sb-thread:make-waitqueue))
1067         (threads nil)
1068         (deadline-handler-run-twice? nil))
1069     (dotimes (i n)
1070       (let ((child
1071              (sb-thread:make-thread
1072               #'(lambda ()
1073                   (handler-bind
1074                       ((sb-sys:deadline-timeout
1075                         (let ((already? nil))
1076                           #'(lambda (c)
1077                               (when already?
1078                                 (setq deadline-handler-run-twice? t))
1079                               (setq already? t)
1080                               (sleep 0.2)
1081                               (sb-thread:condition-broadcast waitq)
1082                               (sb-sys:defer-deadline 10.0 c)))))
1083                     (sb-sys:with-deadline (:seconds 0.1)
1084                       (sb-thread:with-mutex (mutex)
1085                         (sb-thread:condition-wait waitq mutex))))))))
1086         (push child threads)))
1087     (mapc #'sb-thread:join-thread threads)
1088     (assert (not deadline-handler-run-twice?))))
1089
1090 (with-test (:name (:condition-wait :signal-deadline-with-interrupts-enabled))
1091   (let ((mutex (sb-thread:make-mutex))
1092         (waitq (sb-thread:make-waitqueue))
1093         (A-holds? :unknown)
1094         (B-holds? :unknown)
1095         (A-interrupts-enabled? :unknown)
1096         (B-interrupts-enabled? :unknown)
1097         (A)
1098         (B))
1099     ;; W.L.O.G., we assume that A is executed first...
1100     (setq A (sb-thread:make-thread
1101              #'(lambda ()
1102                  (handler-bind
1103                      ((sb-sys:deadline-timeout
1104                        #'(lambda (c)
1105                            ;; We came here through the call to DECODE-TIMEOUT
1106                            ;; in CONDITION-WAIT; hence both here are supposed
1107                            ;; to evaluate to T.
1108                            (setq A-holds? (sb-thread:holding-mutex-p mutex))
1109                            (setq A-interrupts-enabled?
1110                                  sb-sys:*interrupts-enabled*)
1111                            (sleep 0.2)
1112                            (sb-thread:condition-broadcast waitq)
1113                            (sb-sys:defer-deadline 10.0 c))))
1114                    (sb-sys:with-deadline (:seconds 0.1)
1115                      (sb-thread:with-mutex (mutex)
1116                        (sb-thread:condition-wait waitq mutex)))))))
1117     (setq B (sb-thread:make-thread
1118              #'(lambda ()
1119                  (thread-yield)
1120                  (handler-bind
1121                      ((sb-sys:deadline-timeout
1122                        #'(lambda (c)
1123                            ;; We came here through the call to GET-MUTEX
1124                            ;; in CONDITION-WAIT (contended case of
1125                            ;; reaquiring the mutex) - so the former will
1126                            ;; be NIL, but interrupts should still be enabled.
1127                            (setq B-holds? (sb-thread:holding-mutex-p mutex))
1128                            (setq B-interrupts-enabled?
1129                                  sb-sys:*interrupts-enabled*)
1130                            (sleep 0.2)
1131                            (sb-thread:condition-broadcast waitq)
1132                            (sb-sys:defer-deadline 10.0 c))))
1133                    (sb-sys:with-deadline (:seconds 0.1)
1134                      (sb-thread:with-mutex (mutex)
1135                        (sb-thread:condition-wait waitq mutex)))))))
1136     (sb-thread:join-thread A)
1137     (sb-thread:join-thread B)
1138     (let ((A-result (list A-holds? A-interrupts-enabled?))
1139           (B-result (list B-holds? B-interrupts-enabled?)))
1140       ;; We also check some subtle behaviour w.r.t. whether a deadline
1141       ;; handler in CONDITION-WAIT got the mutex, or not. This is most
1142       ;; probably very internal behaviour (so user should not depend
1143       ;; on it) -- I added the testing here just to manifest current
1144       ;; behaviour.
1145       (cond ((equal A-result '(t t)) (assert (equal B-result '(nil t))))
1146             ((equal B-result '(t t)) (assert (equal A-result '(nil t))))
1147             (t (error "Failure: fall through."))))))
1148
1149 (with-test (:name (:mutex :finalization))
1150   (let ((a nil))
1151     (dotimes (i 500000)
1152       (setf a (make-mutex)))))
1153
1154 (format t "mutex finalization test done~%")
1155
1156 ;;; Check that INFO is thread-safe, at least when we're just doing reads.
1157
1158 (let* ((symbols (loop repeat 10000 collect (gensym)))
1159        (functions (loop for (symbol . rest) on symbols
1160                         for next = (car rest)
1161                         for fun = (let ((next next))
1162                                     (lambda (n)
1163                                       (if next
1164                                           (funcall next (1- n))
1165                                           n)))
1166                         do (setf (symbol-function symbol) fun)
1167                         collect fun)))
1168   (defun infodb-test ()
1169     (funcall (car functions) 9999)))
1170
1171 (with-test (:name (:infodb :read))
1172   (let* ((ok t)
1173          (threads (loop for i from 0 to 10
1174                         collect (sb-thread:make-thread
1175                                  (lambda ()
1176                                    (dotimes (j 100)
1177                                      (write-char #\-)
1178                                      (finish-output)
1179                                      (let ((n (infodb-test)))
1180                                        (unless (zerop n)
1181                                          (setf ok nil)
1182                                          (format t "N != 0 (~A)~%" n)
1183                                          (sb-ext:quit)))))))))
1184     (wait-for-threads threads)
1185     (assert ok)))
1186
1187 (format t "infodb test done~%")
1188
1189 (with-test (:name (:backtrace))
1190   ;; Printing backtraces from several threads at once used to hang the
1191   ;; whole SBCL process (discovered by accident due to a timer.impure
1192   ;; test misbehaving). The cause was that packages weren't even
1193   ;; thread-safe for only doing FIND-SYMBOL, and while printing
1194   ;; backtraces a loot of symbol lookups need to be done due to
1195   ;; *PRINT-ESCAPE*.
1196   (let* ((threads (loop repeat 10
1197                         collect (sb-thread:make-thread
1198                                  (lambda ()
1199                                    (dotimes (i 1000)
1200                                      (with-output-to-string (*debug-io*)
1201                                        (sb-debug::backtrace 10))))))))
1202     (wait-for-threads threads)))
1203
1204 (format t "backtrace test done~%")
1205
1206 (format t "~&starting gc deadlock test: WARNING: THIS TEST WILL HANG ON FAILURE!~%")
1207
1208 (with-test (:name (:gc-deadlock))
1209   ;; Prior to 0.9.16.46 thread exit potentially deadlocked the
1210   ;; GC due to *all-threads-lock* and session lock. On earlier
1211   ;; versions and at least on one specific box this test is good enough
1212   ;; to catch that typically well before the 1500th iteration.
1213   (loop
1214      with i = 0
1215      with n = 3000
1216      while (< i n)
1217      do
1218        (incf i)
1219        (when (zerop (mod i 100))
1220          (write-char #\.)
1221          (force-output))
1222        (handler-case
1223            (if (oddp i)
1224                (sb-thread:make-thread
1225                 (lambda ()
1226                   (sleep (random 0.001)))
1227                 :name (format nil "SLEEP-~D" i))
1228                (sb-thread:make-thread
1229                 (lambda ()
1230                   ;; KLUDGE: what we are doing here is explicit,
1231                   ;; but the same can happen because of a regular
1232                   ;; MAKE-THREAD or LIST-ALL-THREADS, and various
1233                   ;; session functions.
1234                   (sb-thread::with-all-threads-lock
1235                     (sb-thread::with-session-lock (sb-thread::*session*)
1236                       (sb-ext:gc))))
1237                 :name (format nil "GC-~D" i)))
1238          (error (e)
1239            (format t "~%error creating thread ~D: ~A -- backing off for retry~%" i e)
1240            (sleep 0.1)
1241            (incf i)))))
1242
1243 (format t "~&gc deadlock test done~%")
1244 \f
1245 (let ((count (make-array 8 :initial-element 0)))
1246   (defun closure-one ()
1247     (declare (optimize safety))
1248     (values (incf (aref count 0)) (incf (aref count 1))
1249             (incf (aref count 2)) (incf (aref count 3))
1250             (incf (aref count 4)) (incf (aref count 5))
1251             (incf (aref count 6)) (incf (aref count 7))))
1252   (defun no-optimizing-away-closure-one ()
1253     (setf count (make-array 8 :initial-element 0))))
1254
1255 (defstruct box
1256   (count 0))
1257
1258 (let ((one (make-box))
1259       (two (make-box))
1260       (three (make-box)))
1261   (defun closure-two ()
1262     (declare (optimize safety))
1263     (values (incf (box-count one)) (incf (box-count two)) (incf (box-count three))))
1264   (defun no-optimizing-away-closure-two ()
1265     (setf one (make-box)
1266           two (make-box)
1267           three (make-box))))
1268
1269 (with-test (:name (:funcallable-instances))
1270   ;; the funcallable-instance implementation used not to be threadsafe
1271   ;; against setting the funcallable-instance function to a closure
1272   ;; (because the code and lexenv were set separately).
1273   (let ((fun (sb-kernel:%make-funcallable-instance 0))
1274         (condition nil))
1275     (setf (sb-kernel:funcallable-instance-fun fun) #'closure-one)
1276     (flet ((changer ()
1277              (loop (setf (sb-kernel:funcallable-instance-fun fun) #'closure-one)
1278                    (setf (sb-kernel:funcallable-instance-fun fun) #'closure-two)))
1279            (test ()
1280              (handler-case (loop (funcall fun))
1281                (serious-condition (c) (setf condition c)))))
1282       (let ((changer (make-thread #'changer))
1283             (test (make-thread #'test)))
1284         (handler-case
1285             (progn
1286               ;; The two closures above are fairly carefully crafted
1287               ;; so that if given the wrong lexenv they will tend to
1288               ;; do some serious damage, but it is of course difficult
1289               ;; to predict where the various bits and pieces will be
1290               ;; allocated.  Five seconds failed fairly reliably on
1291               ;; both my x86 and x86-64 systems.  -- CSR, 2006-09-27.
1292               (sb-ext:with-timeout 5
1293                 (wait-for-threads (list test)))
1294               (error "~@<test thread got condition:~2I~_~A~@:>" condition))
1295           (sb-ext:timeout ()
1296             (terminate-thread changer)
1297             (terminate-thread test)
1298             (wait-for-threads (list changer test))))))))
1299
1300 (format t "~&funcallable-instance test done~%")
1301
1302 (defun random-type (n)
1303   `(integer ,(random n) ,(+ n (random n))))
1304
1305 (defun subtypep-hash-cache-test ()
1306   (dotimes (i 10000)
1307     (let ((type1 (random-type 500))
1308           (type2 (random-type 500)))
1309       (let ((a (subtypep type1 type2)))
1310         (dotimes (i 100)
1311           (assert (eq (subtypep type1 type2) a))))))
1312   (format t "ok~%")
1313   (force-output))
1314
1315 (with-test (:name '(:hash-cache :subtypep))
1316   (dotimes (i 10)
1317     (sb-thread:make-thread #'subtypep-hash-cache-test)))
1318 (format t "hash-cache tests done~%")
1319
1320 ;;;; BLACK BOX TESTS
1321
1322 (in-package :cl-user)
1323 (use-package :test-util)
1324 (use-package "ASSERTOID")
1325
1326 (format t "parallel defclass test -- WARNING, WILL HANG ON FAILURE!~%")
1327 (with-test (:name :parallel-defclass)
1328   (defclass test-1 () ((a :initform :orig-a)))
1329   (defclass test-2 () ((b :initform :orig-b)))
1330   (defclass test-3 (test-1 test-2) ((c :initform :orig-c)))
1331   (let* ((run t)
1332          (d1 (sb-thread:make-thread (lambda ()
1333                                       (loop while run
1334                                             do (defclass test-1 () ((a :initform :new-a)))
1335                                             (write-char #\1)
1336                                             (force-output)))
1337                                     :name "d1"))
1338          (d2 (sb-thread:make-thread (lambda ()
1339                                       (loop while run
1340                                             do (defclass test-2 () ((b :initform :new-b)))
1341                                                (write-char #\2)
1342                                                (force-output)))
1343                                     :name "d2"))
1344          (d3 (sb-thread:make-thread (lambda ()
1345                                       (loop while run
1346                                             do (defclass test-3 (test-1 test-2) ((c :initform :new-c)))
1347                                                (write-char #\3)
1348                                                (force-output)))
1349                                     :name "d3"))
1350          (i (sb-thread:make-thread (lambda ()
1351                                      (loop while run
1352                                            do (let ((i (make-instance 'test-3)))
1353                                                 (assert (member (slot-value i 'a) '(:orig-a :new-a)))
1354                                                 (assert (member (slot-value i 'b) '(:orig-b :new-b)))
1355                                                 (assert (member (slot-value i 'c) '(:orig-c :new-c))))
1356                                               (write-char #\i)
1357                                               (force-output)))
1358                                    :name "i")))
1359     (format t "~%sleeping!~%")
1360     (sleep 2.0)
1361     (format t "~%stopping!~%")
1362     (setf run nil)
1363     (mapc (lambda (th)
1364             (sb-thread:join-thread th)
1365             (format t "~%joined ~S~%" (sb-thread:thread-name th)))
1366           (list d1 d2 d3 i))))
1367 (format t "parallel defclass test done~%")