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
)
318 (test semaphore.typed
319 (is (typep (make-semaphore) 'semaphore
))
320 (is (semaphorep (make-semaphore)))
321 (is (not (semaphorep (make-lock)))))
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 (test semaphore.wait-on-nonzero-creation
330 "Tests that `WAIT-ON-SEMAPHORE` correctly returns T
331 on a smaphore that was initialized to a non-zero value.
332 In other words, it tests that `SIGNAL-SEMAPHORE` is not
333 the only cause that can wake a waiter."
334 (let ((sem (make-semaphore :count
1)))
335 (is-true (wait-on-semaphore sem
:timeout
0))))
337 (test semaphore.wait.timeout
338 (let* ((sem (make-semaphore)))
339 (is-false (wait-on-semaphore sem
:timeout
0))
340 (is-false (wait-on-semaphore sem
:timeout
0.2))))
342 (test semaphore.signal-n-of-m
343 (let* ((sem (make-semaphore :count
1))
347 (wait-on-semaphore sem
)
348 (with-lock-held (lock) (incf count
)))))
349 (make-thread (lambda ()
351 (signal-semaphore sem
:count
3)))
352 (dotimes (v 5) (make-thread waiter
))
355 ;; release other waiters
356 (is (eql t
(signal-semaphore sem
:count
2)))
362 ;;; Condition variables
365 #+#.
(bt2::implemented-p
* 'bt2
:make-condition-variable
)
366 (test condition-variable.typed
367 (is (typep (make-condition-variable) 'condition-variable
))
368 (is (condition-variable-p (make-condition-variable)))
369 (is (not (condition-variable-p (make-lock)))))
371 #+#.
(bt2::implemented-p
* 'bt2
:make-condition-variable
)
372 (test condition-variable.concurrency
374 (let ((cv (make-condition-variable)))
376 (with-lock-held (*lock
*)
379 do
(condition-wait cv
*lock
*)
382 (condition-broadcast cv
)))
383 (let ((num-procs 30))
384 (dotimes (i num-procs
)
385 (let ((i (- num-procs i
1)))
386 (make-thread (lambda ()
388 (funcall #'worker i
))
389 :name
(format nil
"Proc #~D" i
))))
390 (with-lock-held (*lock
*)
392 until
(= num-procs
*shared
*)
393 do
(condition-wait cv
*lock
*)))
394 (is (equal num-procs
*shared
*))))))
396 #+#.
(bt2::implemented-p
* 'bt2
:condition-wait
:timeout
)
397 (test condition-wait.timeout
398 (let ((lock (make-lock))
399 (cv (make-condition-variable))
401 (make-thread (lambda () (sleep 0.4) (setf flag t
)))
402 (with-lock-held (lock)
404 (condition-wait cv lock
:timeout
0.2)))
406 (skip "ABCL's condition-wait always returns T")
413 #+#.
(bt2::implemented-p
* 'bt2
:condition-wait
:timeout
)
414 (test condition-wait.lock-held-on-timeout
415 "Tests that even when `CONDITION-WAIT` times out, it reacquires the
417 (let ((lock (make-lock :name
"Test lock"))
418 (cv (make-condition-variable :name
"Test condition variable")))
419 (with-lock-held (lock)
421 (condition-wait cv lock
:timeout
2)))
423 (skip "ABCL's condition-wait always returns T")
426 ;; We need to test if `lock` is locked, but it must be done in
427 ;; another thread, otherwise it would be a recursive attempt.
428 (let ((res-lock (make-lock :name
"Result lock"))
429 (res-cv (make-condition-variable :name
"Result condition variable"))
430 (lock-was-acquired-p nil
))
431 (make-thread (lambda ()
432 (with-lock-held (res-lock)
433 (setf lock-was-acquired-p
(acquire-lock lock
:wait nil
)))
434 (condition-notify res-cv
)))
435 (with-lock-held (res-lock)
436 (condition-wait res-cv res-lock
)
437 (is-false lock-was-acquired-p
)))))))
439 #+#.
(bt2::implemented-p
* 'bt2
:make-condition-variable
)
440 (test condition-notify.no-waiting-threads
441 "Test that `CONDITION-NOTIFY` returns NIL whether or not there are
443 (let ((lock (make-lock :name
"Test lock"))
444 (cv (make-condition-variable :name
"Test condition variable")))
445 (is-false (condition-notify cv
))
446 (make-thread (lambda ()
447 (with-lock-held (lock)
448 (condition-wait cv lock
))))
449 (is-false (condition-notify cv
))))
451 #+#.
(bt2::implemented-p
* 'bt2
:make-condition-variable
)
452 (test condition-broadcast.return-value
453 "Test that `CONDITION-BROADCAST` returns NIL whether or not there
454 are threads waiting."
455 (let ((lock (make-lock :name
"Test lock"))
456 (cv (make-condition-variable :name
"Test condition variable")))
457 (is-false (condition-notify cv
))
458 (make-thread (lambda ()
459 (with-lock-held (lock)
460 (condition-wait cv lock
)))
461 :name
"Waiting thread 1")
462 (make-thread (lambda ()
463 (with-lock-held (lock)
464 (condition-wait cv lock
)))
465 :name
"Waiting thread 2")
466 (is-false (condition-broadcast cv
))))
473 (test with-timeout.return-value
474 (is (eql :foo
(with-timeout (5) :foo
))))
476 (test with-timeout.signals
477 (signals timeout
(with-timeout (1) (sleep 5))))
479 (test with-timeout.non-interference
480 (flet ((sleep-with-timeout (s)
481 (with-timeout (4) (sleep s
))))
484 (sleep-with-timeout 3)
485 (sleep-with-timeout 3)))))
492 #+(or abcl allegro ccl clisp ecl lispworks sbcl
)
493 (test atomic-integer-incf-decf.return-value
494 (let ((aint (make-atomic-integer :value
0)))
495 (is (= 5 (atomic-integer-incf aint
5)))
496 (is (= 4 (atomic-integer-decf aint
1)))))
498 #+(or abcl allegro ccl clisp ecl lispworks sbcl
)
499 (test atomic-integer-cas.return-value
500 (let ((aint (make-atomic-integer :value
4)))
501 (is (null (atomic-integer-cas aint
0 100)))
502 (is (eql t
(atomic-integer-cas aint
4 7)))))
504 #+(or abcl allegro ccl clisp ecl lispworks sbcl
)
505 (test atomic-integer.concurrency
506 (let* ((aint (make-atomic-integer :value
1000000))
508 (make-thread (lambda ()
510 (atomic-integer-incf aint
)))))
512 (make-thread (lambda ()
514 (atomic-integer-decf aint
))))))
515 (join-thread thread-inc
)
516 (join-thread thread-dec
)
517 (is (= 1000000 (atomic-integer-value aint
)))))