1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: BORDEAUX-THREADS-2/TEST -*-
2 ;;;; The above modeline is required for Genera. Do not change.
4 (in-package :bordeaux-threads-2
/test
)
6 (in-suite :bordeaux-threads-2
)
12 (test join-thread.return-value
13 (is (eql 0 (join-thread (make-thread (lambda () 0))))))
15 (test current-thread.not-null
16 (is (current-thread)))
18 (test current-thread.eql
19 (is (eql (current-thread)
22 #+#.
(bt2::implemented-p
* 'bt2
:join-thread
)
23 (test current-thread.identity
24 (let ((thread (make-thread #'current-thread
)))
25 (is (eql thread
(join-thread thread
)))))
27 #+#.
(bt2::implemented-p
* 'bt2
:join-thread
)
28 (test current-thread.special
29 (let ((thread (make-thread (lambda () bt2
::*current-thread
*))))
30 (is (eql thread
(join-thread thread
)))))
32 #+#.
(bt2::implemented-p
* 'bt2
:join-thread
)
33 (test current-thread.error
34 (let ((thread (make-thread (lambda ()
37 (signals abnormal-exit
(join-thread thread
))))
39 (test threadp.should-identify-threads
40 (is (threadp (current-thread)))
41 (is (threadp (make-thread (lambda () t
))))
42 (is (not (threadp (make-lock)))))
44 (test thread-name.should-retrieve-thread-name
45 (is (equal "foo" (thread-name
46 (make-thread (lambda () t
) :name
"foo")))))
48 (test thread-name.all-strings
49 (is (every #'(lambda (thread) (stringp (thread-name thread
)))
52 (defparameter *some-special
* :global-value
)
54 (test default-special-bindings.sees-global-bindings
55 (let* ((*some-special
* :local-value
)
56 (*default-special-bindings
*
57 `((*some-special
* .
(list :more
*some-special
*))
58 ,@*default-special-bindings
*))
59 (thread (make-thread (lambda () *some-special
*))))
60 (is (equal '(:more
:local-value
) (join-thread thread
)))))
62 (defparameter *shared
* 0)
63 (defparameter *lock
* (make-lock))
65 #+#.
(bt2::implemented-p
* 'bt2
:thread-yield
)
66 (test threads.interaction
67 ;; this simple test generates N process. Each process grabs and
68 ;; releases the lock until SHARED has some value, it then
69 ;; increments SHARED. the outer code first sets shared 1 which
70 ;; gets the thing running and then waits for SHARED to reach some
71 ;; value. this should, i think, stress test locks.
75 do
(with-lock-held (*lock
*)
83 ;; create a new binding to protect against implementations that
84 ;; mutate instead of binding the loop variable
86 (make-thread (lambda ()
88 :name
(format nil
"threads.interaction Proc #~D" i
))))))
89 (with-lock-held (*lock
*)
93 until
(with-lock-held (*lock
*)
94 (= (1+ (length procs
)) *shared
*))
95 do
(with-lock-held (*lock
*)
96 (is (>= (1+ (length procs
)) *shared
*)))
100 (test all-threads.contains-threads
101 (is (every #'threadp
(all-threads))))
103 (test all-threads.contains-new-thread
104 (let ((thread (make-thread (lambda () (sleep 60))
105 :name
"all-threads.contains-new-thread")))
106 (is (find thread
(all-threads)))))
108 #+#.
(bt2::implemented-p
* 'bt2
:interrupt-thread
)
109 (test interrupt-thread.throw
110 (let ((thread (make-thread (lambda ()
114 :name
"interrupt-thread.throw")))
117 (interrupt-thread thread
(lambda ()
118 (throw 'new-thread
'interrupted
)))))
119 (is (eql 'interrupted
(join-thread thread
)))))
121 (test thread-alive-p.new-thread
122 (is (thread-alive-p (make-thread (lambda () (sleep 60))
123 :name
"thread-alive-p.new-thread"))))
125 #+#.
(bt2::implemented-p
* 'bt2
:join-thread
)
126 (test thread-termination.unwind-protect
127 (setf *some-special
* nil
)
129 (skip "DESTROY-THREAD does not execute UNWIND-PROTECT cleanup forms.
130 Filed https://github.com/armedbear/abcl/issues/430.")
133 (setf *some-special
* :entered
)
137 (setf *some-special
* :failed
))
138 (when (eq *some-special
* :entered
)
139 (setf *some-special
* :success
)))))
140 (let ((thread (make-thread #'thread-fn
)))
142 (destroy-thread thread
)
143 (signals abnormal-exit
144 (join-thread thread
))
145 (is (eq :success
*some-special
*)))))
147 (define-condition test-error
(error) ())
149 #+#.
(bt2::implemented-p
* 'bt2
:join-thread
)
150 (test thread-termination.handle-condition
152 (error 'test-error
)))
153 (let ((thread (make-thread #'thread-fn
:trap-conditions t
)))
157 (is (typep (abnormal-exit-condition e
) 'test-error
)))))))
159 #+#.
(bt2::implemented-p
* 'bt2
:destroy-thread
)
160 (test destroy-thread.terminates
161 (let ((thread (make-thread (lambda () (sleep 3))
162 :name
"destroy-thread.terminates")))
163 (is (threadp (destroy-thread thread
)))
165 (is-false (thread-alive-p thread
))))
167 #+#.
(bt2::implemented-p
* 'bt2
:destroy-thread
)
168 (test join-thread.error-if-destroyed
169 (let ((thread (make-thread (lambda () (sleep 3))
170 :name
"join-thread.error-if-destroyed")))
171 (destroy-thread thread
)
172 (signals abnormal-exit
(join-thread thread
))))
174 #+#.
(bt2::implemented-p
* 'bt2
:destroy-thread
)
175 (test destroy-thread.error-if-exited
176 (let ((thread (make-thread (lambda () (sleep 3))
177 :name
"destroy-thread.error-if-exited")))
179 (signals bordeaux-threads-error
(destroy-thread thread
))))
183 ;;; Non-recursive Locks
186 (test lock.constructor
187 (let ((lock (make-lock :name
"Name")))
189 (is (native-lock-p (lock-native-lock lock
)))
190 (is (equal "Name" (lock-name lock
)))))
192 (test acquire-lock.no-contention
193 (let ((lock (make-lock)))
194 (is (acquire-lock lock
:wait t
))
195 (is (lockp (release-lock lock
)))
196 (is (acquire-lock lock
:wait nil
))
197 (is (lockp (release-lock lock
)))))
199 (test acquire-lock.try-lock
200 (let ((lock (make-lock)))
201 (make-thread (lambda ()
202 (with-lock-held (lock)
204 :name
"acquire-lock.try-lock")
206 (is-false (acquire-lock lock
:wait nil
))))
208 (test acquire-lock.timeout-expires
209 (let ((lock (make-lock)))
210 (make-thread (lambda ()
211 (with-lock-held (lock)
213 :name
"acquire-lock.timeout-expires")
215 (is (null (acquire-lock lock
:timeout
.1)))))
217 #+#.
(bt2::implemented-p
* 'bt2
:with-lock-held
)
218 (test with-lock-held.timeout-no-contention-acquired
219 (let ((lock (make-lock)))
220 (is (eql :ok
(with-lock-held (lock :timeout
.1) :ok
)))))
222 #+#.
(bt2::implemented-p
* 'bt2
:with-lock-held
)
223 (test with-lock-held.timeout-expires
224 (let ((lock (make-lock)))
225 (make-thread (lambda ()
226 (with-lock-held (lock)
228 :name
"with-lock-held.timeout-expires")
232 (with-lock-held (lock :timeout
.1)
233 (return-from ok
:ok
))
240 #+#.
(bt2::implemented-p
* 'bt2
:acquire-recursive-lock
)
241 (test acquire-recursive-lock
242 (let ((test-lock (make-recursive-lock))
243 (results (make-array 4 :adjustable t
:fill-pointer
0))
244 (results-lock (make-lock))
246 (flet ((add-result (r)
247 (with-lock-held (results-lock)
248 (vector-push-extend r results
))))
252 (when (acquire-recursive-lock test-lock
)
258 (release-recursive-lock test-lock
))))
259 :name
(format nil
"acquire-recursive-lock Proc #~D" i
))
261 (map 'nil
#'join-thread threads
)
262 (is (equalp #(:enter
:leave
:enter
:leave
) results
))))
264 #+#.
(bt2::implemented-p
* 'bt2
:acquire-recursive-lock
)
265 (test acquire-recursive-lock.no-contention
266 (let ((lock (make-recursive-lock)))
267 (is (acquire-recursive-lock lock
:wait t
))
268 (is (recursive-lock-p (release-recursive-lock lock
)))
269 (is (acquire-recursive-lock lock
:wait nil
))
270 (is (recursive-lock-p (release-recursive-lock lock
)))))
272 #+#.
(bt2::implemented-p
* 'bt2
:with-recursive-lock-held
)
273 (test acquire-recursive-lock.try-lock
274 (let ((lock (make-recursive-lock)))
275 (make-thread (lambda ()
276 (with-recursive-lock-held (lock)
278 :name
"acquire-recursive-lock.try-lock")
280 (is (null (acquire-recursive-lock lock
:wait nil
)))))
282 #+#.
(bt2::implemented-p
* 'bt2
:with-recursive-lock-held
)
283 (test acquire-recursive-lock.timeout-expires
284 (let ((lock (make-recursive-lock)))
285 (make-thread (lambda ()
286 (with-recursive-lock-held (lock)
288 :name
"acquire-recursive-lock.timeout-expires")
290 (is (null (acquire-recursive-lock lock
:timeout
.1)))))
292 #+#.
(bt2::implemented-p
* 'bt2
:with-recursive-lock-held
)
293 (test with-recursive-lock-held.timeout-no-contention-acquired
294 (let ((lock (make-recursive-lock)))
295 (is (eql :ok
(with-recursive-lock-held (lock :timeout
.1) :ok
)))))
297 #+#.
(bt2::implemented-p
* 'bt2
:with-recursive-lock-held
)
298 (test with-recursive-lock-held.timeout-expires
299 (let ((lock (make-recursive-lock)))
300 (make-thread (lambda ()
301 (with-recursive-lock-held (lock)
303 :name
"with-recursive-lock-held.timeout-expires")
307 (with-recursive-lock-held (lock :timeout
.1)
308 (return-from ok
:ok
))
316 #+#.
(bt2::implemented-p
* 'bt2
:make-semaphore
)
317 (test semaphore.typed
318 (is (typep (make-semaphore) 'semaphore
))
319 (is (semaphorep (make-semaphore)))
320 (is (not (semaphorep (make-lock)))))
322 #+#.
(bt2::implemented-p
* 'bt2
:signal-semaphore
)
323 (test semaphore.signal
324 (let ((sem (make-semaphore)))
325 (make-thread (lambda () (sleep 0.4) (signal-semaphore sem
)))
326 (is-true (wait-on-semaphore sem
))
327 (is-true (signal-semaphore sem
))))
329 #+#.
(bt2::implemented-p
* 'bt2
:wait-on-semaphore
:timeout
)
330 (test semaphore.wait-on-nonzero-creation
331 "Tests that `WAIT-ON-SEMAPHORE` correctly returns T
332 on a smaphore that was initialized to a non-zero value.
333 In other words, it tests that `SIGNAL-SEMAPHORE` is not
334 the only cause that can wake a waiter."
335 (let ((sem (make-semaphore :count
1)))
336 (is-true (wait-on-semaphore sem
:timeout
0))))
338 #+#.
(bt2::implemented-p
* 'bt2
:wait-on-semaphore
:timeout
)
339 (test semaphore.wait.timeout
340 (let* ((sem (make-semaphore)))
341 (is-false (wait-on-semaphore sem
:timeout
0))
342 (is-false (wait-on-semaphore sem
:timeout
0.2))))
344 #+#.
(bt2::implemented-p
* 'bt2
:wait-on-semaphore
)
345 (test semaphore.signal-n-of-m
346 (let* ((sem (make-semaphore :count
1))
350 (wait-on-semaphore sem
)
351 (with-lock-held (lock) (incf count
)))))
352 (make-thread (lambda ()
354 (signal-semaphore sem
:count
3)))
355 (dotimes (v 5) (make-thread waiter
))
358 ;; release other waiters
359 (is (eql t
(signal-semaphore sem
:count
2)))
365 ;;; Condition variables
368 #+#.
(bt2::implemented-p
* 'bt2
:make-condition-variable
)
369 (test condition-variable.typed
370 (is (typep (make-condition-variable) 'condition-variable
))
371 (is (condition-variable-p (make-condition-variable)))
372 (is (not (condition-variable-p (make-lock)))))
374 #+#.
(bt2::implemented-p
* 'bt2
:condition-broadcast
)
375 (test condition-variable.concurrency
377 (let ((cv (make-condition-variable)))
379 (with-lock-held (*lock
*)
382 do
(condition-wait cv
*lock
*)
385 (condition-broadcast cv
)))
386 (let ((num-procs 30))
387 (dotimes (i num-procs
)
388 (let ((i (- num-procs i
1)))
389 (make-thread (lambda ()
391 (funcall #'worker i
))
392 :name
(format nil
"Proc #~D" i
))))
393 (with-lock-held (*lock
*)
395 until
(= num-procs
*shared
*)
396 do
(condition-wait cv
*lock
*)))
397 (is (equal num-procs
*shared
*))))))
399 #+#.
(bt2::implemented-p
* 'bt2
:condition-wait
:timeout
)
400 (test condition-wait.timeout
401 (let ((lock (make-lock))
402 (cv (make-condition-variable))
404 (make-thread (lambda () (sleep 0.4) (setf flag t
)))
405 (with-lock-held (lock)
407 (condition-wait cv lock
:timeout
0.2)))
409 (skip "ABCL's condition-wait always returns T")
416 #+#.
(bt2::implemented-p
* 'bt2
:condition-wait
:timeout
)
417 (test condition-wait.lock-held-on-timeout
418 "Tests that even when `CONDITION-WAIT` times out, it reacquires the
420 (let ((lock (make-lock :name
"Test lock"))
421 (cv (make-condition-variable :name
"Test condition variable")))
422 (with-lock-held (lock)
424 (condition-wait cv lock
:timeout
2)))
426 (skip "ABCL's condition-wait always returns T")
429 ;; We need to test if `lock` is locked, but it must be done in
430 ;; another thread, otherwise it would be a recursive attempt.
431 (let ((res-lock (make-lock :name
"Result lock"))
432 (res-cv (make-condition-variable :name
"Result condition variable"))
433 (lock-was-acquired-p nil
))
434 (make-thread (lambda ()
435 (with-lock-held (res-lock)
436 (setf lock-was-acquired-p
(acquire-lock lock
:wait nil
)))
437 (condition-notify res-cv
)))
438 (with-lock-held (res-lock)
439 (condition-wait res-cv res-lock
)
440 (is-false lock-was-acquired-p
)))))))
442 #+#.
(bt2::implemented-p
* 'bt2
:condition-notify
)
443 (test condition-notify.no-waiting-threads
444 "Test that `CONDITION-NOTIFY` returns NIL whether or not there are
446 (let ((lock (make-lock :name
"Test lock"))
447 (cv (make-condition-variable :name
"Test condition variable")))
448 (is-false (condition-notify cv
))
449 (make-thread (lambda ()
450 (with-lock-held (lock)
451 (condition-wait cv lock
))))
452 (is-false (condition-notify cv
))))
454 #+#.
(bt2::implemented-p
* 'bt2
:condition-broadcast
)
455 (test condition-broadcast.return-value
456 "Test that `CONDITION-BROADCAST` returns NIL whether or not there
457 are threads waiting."
458 (let ((lock (make-lock :name
"Test lock"))
459 (cv (make-condition-variable :name
"Test condition variable")))
460 (is-false (condition-notify cv
))
461 (make-thread (lambda ()
462 (with-lock-held (lock)
463 (condition-wait cv lock
)))
464 :name
"Waiting thread 1")
465 (make-thread (lambda ()
466 (with-lock-held (lock)
467 (condition-wait cv lock
)))
468 :name
"Waiting thread 2")
469 (is-false (condition-broadcast cv
))))
476 (test with-timeout.return-value
477 (is (eql :foo
(with-timeout (5) :foo
))))
479 (test with-timeout.signals
480 (signals timeout
(with-timeout (1) (sleep 5))))
482 (test with-timeout.non-interference
483 (flet ((sleep-with-timeout (s)
484 (with-timeout (4) (sleep s
))))
487 (sleep-with-timeout 3)
488 (sleep-with-timeout 3)))))
495 #+(or abcl allegro ccl clisp cmu ecl lispworks sbcl
)
496 (test atomic-integer-incf-decf.return-value
497 (let ((aint (make-atomic-integer :value
0)))
498 (is (= 5 (atomic-integer-incf aint
5)))
499 (is (= 4 (atomic-integer-decf aint
1)))))
501 #+(or abcl allegro ccl clisp cmu ecl lispworks sbcl
)
502 (test atomic-integer-compare-and-swap.return-value
503 (let ((aint (make-atomic-integer :value
4)))
504 (is (null (atomic-integer-compare-and-swap aint
0 100)))
505 (is (eql t
(atomic-integer-compare-and-swap aint
4 7)))))
507 #+(or abcl allegro ccl clisp cmu ecl lispworks sbcl
)
508 (test atomic-integer.concurrency
509 (let* ((aint (make-atomic-integer :value
1000000))
511 (make-thread (lambda ()
513 (atomic-integer-incf aint
)))))
515 (make-thread (lambda ()
517 (atomic-integer-decf aint
))))))
518 (join-thread thread-inc
)
519 (join-thread thread-dec
)
520 (is (= 1000000 (atomic-integer-value aint
)))))