1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: CL-USER -*-
2 ;;;; The above modeline is required for Genera. Do not change.
5 Copyright
2006,2007 Greg Pfeil
7 Distributed under the MIT license
(see LICENSE file
)
10 (defpackage bordeaux-threads
/test
11 (:use
#:cl
#:bordeaux-threads
#:fiveam
)
12 (:shadow
#:with-timeout
))
14 (in-package #:bordeaux-threads
/test
)
16 (def-suite :bordeaux-threads
)
17 (def-fixture using-lock
()
18 (let ((lock (make-lock)))
20 (in-suite :bordeaux-threads
)
22 (test should-have-current-thread
23 (is (current-thread)))
25 (test current-thread-identity
26 (let* ((box (list nil
))
27 (thread (make-thread (lambda ()
28 (setf (car box
) (current-thread))))))
30 (is (eql (car box
) thread
))))
32 (test current-thread-eql
33 (is (eql (current-thread)
36 (test join-thread-return-value
37 (is (eql 0 (join-thread (make-thread (lambda () 0))))))
39 (test should-identify-threads-correctly
40 (is (threadp (current-thread)))
41 (is (threadp (make-thread (lambda () t
) :name
"foo")))
42 (is (not (threadp (make-lock)))))
44 (test should-retrieve-thread-name
45 (is (equal "foo" (thread-name (make-thread (lambda () t
) :name
"foo")))))
47 (test interrupt-thread
48 (let* ((box (list nil
))
49 (thread (make-thread (lambda ()
53 'not-interrupted
))))))
55 (interrupt-thread thread
(lambda ()
56 (throw 'new-thread
'interrupted
)))
58 (is (eql 'interrupted
(car box
)))))
60 (test should-lock-without-contention
61 (with-fixture using-lock
()
62 (is (acquire-lock lock t
))
64 (is (acquire-lock lock nil
))
68 (def-test acquire-recursive-lock
()
69 (let ((test-lock (make-recursive-lock))
70 (results (make-array 4 :adjustable t
:fill-pointer
0))
71 (results-lock (make-lock))
73 (flet ((add-result (r)
74 (with-lock-held (results-lock)
75 (vector-push-extend r results
))))
79 (when (acquire-recursive-lock test-lock
)
85 (release-recursive-lock test-lock
)))))
87 (map 'nil
#'join-thread threads
)
88 (is (equalp results
#(:enter
:leave
:enter
:leave
)))))
90 (defun set-equal (set-a set-b
)
91 (and (null (set-difference set-a set-b
))
92 (null (set-difference set-b set-a
))))
94 (test default-special-bindings
95 (locally (declare (special *a
* *c
*))
96 (let* ((the-as 50) (the-bs 150) (*b
* 42)
97 some-a some-b some-other-a some-other-b
98 (*default-special-bindings
*
99 `((*a
* .
(funcall ,(lambda () (incf the-as
))))
100 (*b
* .
(funcall ,(lambda () (incf the-bs
))))
101 ,@*default-special-bindings
*))
102 (threads (list (make-thread
104 (setf some-a
*a
* some-b
*b
*)))
107 (setf some-other-a
*a
*
108 some-other-b
*b
*))))))
109 (declare (special *b
*))
111 (is (not (boundp '*a
*)))
112 (loop while
(some #'thread-alive-p threads
)
114 (is (set-equal (list some-a some-other-a
) '(51 52)))
115 (is (set-equal (list some-b some-other-b
) '(151 152)))
116 (is (not (boundp '*a
*))))))
119 (defparameter *shared
* 0)
120 (defparameter *lock
* (make-lock))
122 (test should-have-thread-interaction
123 ;; this simple test generates N process. Each process grabs and
124 ;; releases the lock until SHARED has some value, it then
125 ;; increments SHARED. the outer code first sets shared 1 which
126 ;; gets the thing running and then waits for SHARED to reach some
127 ;; value. this should, i think, stress test locks.
131 do
(with-lock-held (*lock
*)
139 ;; create a new binding to protect against implementations that
140 ;; mutate instead of binding the loop variable
142 (make-thread (lambda ()
143 (funcall #'worker i
))
144 :name
(format nil
"Proc #~D" i
))))))
145 (with-lock-held (*lock
*)
149 until
(with-lock-held (*lock
*)
150 (= (1+ (length procs
)) *shared
*))
151 do
(with-lock-held (*lock
*)
152 (is (>= (1+ (length procs
)) *shared
*)))
157 ;; Generally safe sanity check for the locks and single-notify
158 #+(and lispworks
(or lispworks4 lispworks5
))
159 (test condition-variable-lw
160 (let ((condition-variable (make-condition-variable :name
"Test"))
161 (test-lock (make-lock))
165 (make-thread (lambda ()
166 (with-lock-held (test-lock)
167 (condition-wait condition-variable test-lock
)
169 (condition-notify condition-variable
))))))
172 (print "Failed: Premature passage through condition-wait")
173 (print "Successfully waited on condition"))
174 (condition-notify condition-variable
)
177 (eql (length completed
) 6)
178 (equal (sort completed
#'<)
179 (loop for id from
0 to
5 collect id
)))
180 (print "Success: All elements notified")
181 (print (format nil
"Failed: Of 6 expected elements, only ~A proceeded" completed
)))
182 (bt::with-cv-access condition-variable
184 (not (or (car wait-tlist
) (cdr wait-tlist
)))
185 (zerop (hash-table-count wait-hash
))
186 (zerop (hash-table-count unconsumed-notifications
)))
187 (print "Success: condition variable restored to initial state")
188 (print "Error: condition variable retains residue from completed waiters")))
192 (make-thread (lambda ()
193 (with-lock-held (test-lock)
194 (condition-wait condition-variable test-lock
)
195 (push id completed
))))))
197 (condition-notify condition-variable
)
199 (if (= (length completed
) 1)
200 (print "Success: Notify-single only notified a single waiter to restart")
201 (format t
"Failure: Notify-single restarted ~A items" (length completed
)))
202 (condition-notify condition-variable
)
204 (if (= (length completed
) 2)
205 (print "Success: second Notify-single only notified a single waiter to restart")
206 (format t
"Failure: Two Notify-singles restarted ~A items" (length completed
)))
207 (loop for i from
0 to
5 do
(condition-notify condition-variable
))
208 (print "Note: In the case of any failures, assume there are outstanding waiting threads")
211 #+(or abcl allegro clisp clozure ecl genera lispworks6 mezzano sbcl scl
)
212 (test condition-wait-timeout
213 (let ((lock (make-lock))
214 (cvar (make-condition-variable))
216 (make-thread (lambda () (sleep 0.4) (setf flag t
)))
217 (with-lock-held (lock)
218 (condition-wait cvar lock
:timeout
0.2)
223 (test semaphore-signal
224 (let ((sem (make-semaphore)))
225 (make-thread (lambda () (sleep 0.4) (signal-semaphore sem
)))
226 (is (not (null (wait-on-semaphore sem
))))))
228 (test semaphore-signal-n-of-m
229 (let* ((sem (make-semaphore :count
1))
233 (wait-on-semaphore sem
)
234 (with-lock-held (lock) (incf count
)))))
235 (make-thread (lambda () (sleep 0.2) (signal-semaphore sem
:count
3)))
236 (dotimes (v 5) (make-thread waiter
))
239 ;; release other waiters
240 (signal-semaphore sem
:count
10)
244 (test semaphore-wait-timeout
245 (let ((sem (make-semaphore))
247 (make-thread (lambda () (sleep 3) (setf flag t
)))
248 (is (null (wait-on-semaphore sem
:timeout
0.2)))
253 (test semaphore-typed
254 (is (typep (bt:make-semaphore
) 'bt
:semaphore
))
255 (is (bt:semaphore-p
(bt:make-semaphore
)))
256 (is (null (bt:semaphore-p
(bt:make-lock
)))))
258 (test with-timeout-return-value
259 (is (eql :foo
(bt:with-timeout
(5) :foo
))))
261 (test with-timeout-signals
262 (signals timeout
(bt:with-timeout
(1) (sleep 5))))
264 (test with-timeout-non-interference
265 (flet ((sleep-with-timeout (s)
266 (bt:with-timeout
(4) (sleep s
))))
269 (sleep-with-timeout 3)
270 (sleep-with-timeout 3)))))