Scheme SRFI-18 implementation and tests file
[bpt/guile.git] / test-suite / tests / srfi-18.test
CommitLineData
b046219e
JG
1;;;; srfi-18.test --- Test suite for Guile's SRFI-18 functions. -*- scheme -*-
2;;;; Julian Graham, 2007-10-26
3;;;;
4;;;; Copyright (C) 2007 Free Software Foundation, Inc.
5;;;;
6;;;; This program is free software; you can redistribute it and/or modify
7;;;; it under the terms of the GNU General Public License as published by
8;;;; the Free Software Foundation; either version 2, or (at your option)
9;;;; any later version.
10;;;;
11;;;; This program is distributed in the hope that it will be useful,
12;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;;; GNU General Public License for more details.
15;;;;
16;;;; You should have received a copy of the GNU General Public License
17;;;; along with this software; see the file COPYING. If not, write to
18;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19;;;; Boston, MA 02110-1301 USA
20
21(define-module (test-suite test-srfi-18)
22 #:use-module (test-suite lib)
23 #:use-module (srfi srfi-18))
24
25(with-test-prefix "current-thread"
26
27 (pass-if "current-thread eq current-thread"
28 (eq? (current-thread) (current-thread))))
29
30(with-test-prefix "thread?"
31
32 (pass-if "current-thread is thread"
33 (thread? (current-thread)))
34
35 (pass-if "foo not thread"
36 (not (thread? 'foo))))
37
38(with-test-prefix "make-thread"
39
40 (pass-if "make-thread creates new thread"
41 (let* ((n (length (all-threads)))
42 (t (make-thread (lambda () 'foo) 'make-thread-1))
43 (r (> (length (all-threads)) n)))
44 (thread-terminate! t) r)))
45
46(with-test-prefix "thread-name"
47
48 (pass-if "make-thread with name binds name"
49 (let* ((t (make-thread (lambda () 'foo) 'thread-name-1))
50 (r (eq? (thread-name t) 'thread-name-1)))
51 (thread-terminate! t) r))
52
53 (pass-if "make-thread without name does not bind name"
54 (let* ((t (make-thread (lambda () 'foo)))
55 (r (not (thread-name t))))
56 (thread-terminate! t) r)))
57
58(with-test-prefix "thread-specific"
59
60 (pass-if "thread-specific is initially #f"
61 (let* ((t (make-thread (lambda () 'foo) 'thread-specific-1))
62 (r (not (thread-specific t))))
63 (thread-terminate! t) r))
64
65 (pass-if "thread-specific-set! can set value"
66 (let ((t (make-thread (lambda () 'foo) 'thread-specific-2)))
67 (thread-specific-set! t "hello")
68 (let ((r (equal? (thread-specific t) "hello")))
69 (thread-terminate! t) r))))
70
71(with-test-prefix "thread-start!"
72
73 (pass-if "thread activates only after start"
74 (let* ((started #f)
75 (m (make-mutex 'thread-start-mutex))
76 (t (make-thread (lambda () (set! started #t)) 'thread-start-1)))
77 (and (not started) (thread-start! t) (thread-join! t) started))))
78
79(with-test-prefix "thread-yield!"
80
81 (pass-if "thread yield suceeds"
82 (thread-yield!) #t))
83
84(with-test-prefix "thread-sleep!"
85
86 (pass-if "thread sleep with time"
87 (let ((future-time (seconds->time (+ (time->seconds (current-time)) 2))))
88 (unspecified? (thread-sleep! future-time))))
89
90 (pass-if "thread sleep with number"
91 (let ((old-secs (car (current-time))))
92 (unspecified? (thread-sleep! (+ (time->seconds (current-time)))))))
93
94 (pass-if "thread does not sleep on past time"
95 (let ((past-time (seconds->time (- (time->seconds (current-time)) 2))))
96 (unspecified? (thread-sleep! past-time)))))
97
98(with-test-prefix "thread-terminate!"
99
100 (pass-if "termination destroys non-started thread"
101 (let ((t (make-thread (lambda () 'nothing) 'thread-terminate-1))
102 (num-threads (length (all-threads)))
103 (success #f))
104 (thread-terminate! t)
105 (with-exception-handler
106 (lambda (obj) (set! success (terminated-thread-exception? obj)))
107 (lambda () (thread-join! t)))
108 success))
109
110 (pass-if "termination destroys started thread"
111 (let* ((m1 (make-mutex 'thread-terminate-2a))
112 (m2 (make-mutex 'thread-terminate-2b))
113 (c (make-condition-variable 'thread-terminate-2))
114 (t (make-thread (lambda ()
115 (mutex-lock! m1)
116 (condition-variable-signal! c)
117 (mutex-unlock! m1)
118 (mutex-lock! m2))
119 'thread-terminate-2))
120 (success #f))
121 (mutex-lock! m1)
122 (mutex-lock! m2)
123 (thread-start! t)
124 (mutex-unlock! m1 c)
125 (thread-terminate! t)
126 (with-exception-handler
127 (lambda (obj) (set! success (terminated-thread-exception? obj)))
128 (lambda () (thread-join! t)))
129 success)))
130
131(with-test-prefix "thread-join!"
132
133 (pass-if "join receives result of thread"
134 (let ((t (make-thread (lambda () 'foo) 'thread-join-1)))
135 (thread-start! t)
136 (eq? (thread-join! t) 'foo)))
137
138 (pass-if "join receives timeout val if timeout expires"
139 (let* ((m (make-mutex 'thread-join-2))
140 (t (make-thread (lambda () (mutex-lock! m)) 'thread-join-2)))
141 (mutex-lock! m)
142 (thread-start! t)
143 (let ((r (thread-join! t (current-time) 'bar)))
144 (thread-terminate! t)
145 (eq? r 'bar))))
146
147 (pass-if "join throws exception on timeout without timeout val"
148 (let* ((m (make-mutex 'thread-join-3))
149 (t (make-thread (lambda () (mutex-lock! m)) 'thread-join-3))
150 (success #f))
151 (mutex-lock! m)
152 (thread-start! t)
153 (with-exception-handler
154 (lambda (obj) (set! success (join-timeout-exception? obj)))
155 (lambda () (thread-join! t (current-time))))
156 (thread-terminate! t)
157 success))
158
159 (pass-if "join waits on timeout"
160 (let ((t (make-thread (lambda () (sleep 1) 'foo) 'thread-join-4)))
161 (thread-start! t)
162 (eq? (thread-join! t (+ (time->seconds (current-time)) 2)) 'foo))))
163
164(with-test-prefix "mutex?"
165
166 (pass-if "make-mutex creates mutex"
167 (mutex? (make-mutex)))
168
169 (pass-if "symbol not mutex"
170 (not (mutex? 'foo))))
171
172(with-test-prefix "mutex-name"
173
174 (pass-if "make-mutex with name binds name"
175 (let* ((m (make-mutex 'mutex-name-1)))
176 (eq? (mutex-name m) 'mutex-name-1)))
177
178 (pass-if "make-mutex without name does not bind name"
179 (let* ((m (make-mutex)))
180 (not (mutex-name m)))))
181
182(with-test-prefix "mutex-specific"
183
184 (pass-if "mutex-specific is initially #f"
185 (let ((m (make-mutex 'mutex-specific-1)))
186 (not (mutex-specific m))))
187
188 (pass-if "mutex-specific-set! can set value"
189 (let ((m (make-mutex 'mutex-specific-2)))
190 (mutex-specific-set! m "hello")
191 (equal? (mutex-specific m) "hello"))))
192
193(with-test-prefix "mutex-state"
194
195 (pass-if "mutex state is initially not-abandoned"
196 (let ((m (make-mutex 'mutex-state-1)))
197 (eq? (mutex-state m) 'not-abandoned)))
198
199 (pass-if "mutex state of locked, owned mutex is owner thread"
200 (let ((m (make-mutex 'mutex-state-2)))
201 (mutex-lock! m)
202 (eq? (mutex-state m) (current-thread))))
203
204 (pass-if "mutex state of locked, unowned mutex is not-owned"
205 (let ((m (make-mutex 'mutex-state-3)))
206 (mutex-lock! m #f #f)
207 (eq? (mutex-state m) 'not-owned)))
208
209 (pass-if "mutex state of unlocked, abandoned mutex is abandoned"
210 (let* ((m (make-mutex 'mutex-state-4))
211 (t (make-thread (lambda () (mutex-lock! m)))))
212 (thread-start! t)
213 (thread-join! t)
214 (eq? (mutex-state m) 'abandoned))))
215
216(with-test-prefix "mutex-lock!"
217
218 (pass-if "mutex-lock! returns true on successful lock"
219 (let* ((m (make-mutex 'mutex-lock-1)))
220 (mutex-lock! m)))
221
222 (pass-if "mutex-lock! returns false on timeout"
223 (let* ((m (make-mutex 'mutex-lock-2))
224 (t (make-thread (lambda () (mutex-lock! m (current-time) #f)))))
225 (mutex-lock! m)
226 (thread-start! t)
227 (not (thread-join! t))))
228
229 (pass-if "mutex-lock! returns true when lock obtained within timeout"
230 (let* ((m (make-mutex 'mutex-lock-3))
231 (t (make-thread (lambda ()
232 (mutex-lock! m (+ (time->seconds (current-time))
233 100)
234 #f)))))
235 (mutex-lock! m)
236 (thread-start! t)
237 (mutex-unlock! m)
238 (thread-join! t)))
239
240 (pass-if "can lock mutex for non-current thread"
241 (let* ((m1 (make-mutex 'mutex-lock-4a))
242 (m2 (make-mutex 'mutex-lock-4b))
243 (t (make-thread (lambda () (mutex-lock! m1)) 'mutex-lock-4)))
244 (mutex-lock! m1)
245 (thread-start! t)
246 (mutex-lock! m2 #f t)
247 (let ((success (eq? (mutex-state m2) t)))
248 (thread-terminate! t) success)))
249
250 (pass-if "locking abandoned mutex throws exception"
251 (let* ((m (make-mutex 'mutex-lock-5))
252 (t (make-thread (lambda () (mutex-lock! m)) 'mutex-lock-5))
253 (success #f))
254 (thread-start! t)
255 (thread-join! t)
256 (with-exception-handler
257 (lambda (obj) (set! success (abandoned-mutex-exception? obj)))
258 (lambda () (mutex-lock! m)))
259 (and success (eq? (mutex-state m) (current-thread)))))
260
261 (pass-if "sleeping threads notified of abandonment"
262 (let* ((m1 (make-mutex 'mutex-lock-6a))
263 (m2 (make-mutex 'mutex-lock-6b))
264 (c (make-condition-variable 'mutex-lock-6))
265 (t (make-thread (lambda ()
266 (mutex-lock! m1)
267 (mutex-lock! m2)
268 (condition-variable-signal! c))))
269 (success #f))
270 (mutex-lock! m1)
271 (thread-start! t)
272 (with-exception-handler
273 (lambda (obj) (set! success (abandoned-mutex-exception? obj)))
274 (lambda () (mutex-unlock! m1 c) (mutex-lock! m2)))
275 success)))
276
277(with-test-prefix "mutex-unlock!"
278
279 (pass-if "unlock changes mutex state"
280 (let* ((m (make-mutex 'mutex-unlock-1)))
281 (mutex-lock! m)
282 (mutex-unlock! m)
283 (eq? (mutex-state m) 'not-abandoned)))
284
285 (pass-if "can unlock from any thread"
286 (let* ((m (make-mutex 'mutex-unlock-2))
287 (t (make-thread (lambda () (mutex-unlock! m)) 'mutex-unlock-2)))
288 (mutex-lock! m)
289 (thread-start! t)
290 (thread-join! t)
291 (eq? (mutex-state m) 'not-abandoned)))
292
293 (pass-if "mutex unlock is true when condition is signalled"
294 (let* ((m (make-mutex 'mutex-unlock-3))
295 (c (make-condition-variable 'mutex-unlock-3))
296 (t (make-thread (lambda ()
297 (mutex-lock! m)
298 (condition-variable-signal! c)
299 (mutex-unlock! m)))))
300 (mutex-lock! m)
301 (thread-start! t)
302 (mutex-unlock! m c)))
303
304 (pass-if "mutex unlock is false when condition times out"
305 (let* ((m (make-mutex 'mutex-unlock-4))
306 (c (make-condition-variable 'mutex-unlock-4)))
307 (mutex-lock! m)
308 (not (mutex-unlock! m c (+ (time->seconds (current-time)) 1))))))
309
310(with-test-prefix "condition-variable?"
311
312 (pass-if "make-condition-variable creates condition variable"
313 (condition-variable? (make-condition-variable)))
314
315 (pass-if "symbol not condition variable"
316 (not (condition-variable? 'foo))))
317
318(with-test-prefix "condition-variable-name"
319
320 (pass-if "make-condition-variable with name binds name"
321 (let* ((c (make-condition-variable 'condition-variable-name-1)))
322 (eq? (condition-variable-name c) 'condition-variable-name-1)))
323
324 (pass-if "make-condition-variable without name does not bind name"
325 (let* ((c (make-condition-variable)))
326 (not (condition-variable-name c)))))
327
328(with-test-prefix "condition-variable-specific"
329
330 (pass-if "condition-variable-specific is initially #f"
331 (let ((c (make-condition-variable 'condition-variable-specific-1)))
332 (not (condition-variable-specific c))))
333
334 (pass-if "condition-variable-specific-set! can set value"
335 (let ((c (make-condition-variable 'condition-variable-specific-1)))
336 (condition-variable-specific-set! c "hello")
337 (equal? (condition-variable-specific c) "hello"))))
338
339(with-test-prefix "condition-variable-signal!"
340
341 (pass-if "condition-variable-signal! wakes up single thread"
342 (let* ((m (make-mutex 'condition-variable-signal-1))
343 (c (make-condition-variable 'condition-variable-signal-1))
344 (t (make-thread (lambda ()
345 (mutex-lock! m)
346 (condition-variable-signal! c)
347 (mutex-unlock! m)))))
348 (mutex-lock! m)
349 (thread-start! t)
350 (mutex-unlock! m c))))
351
352(with-test-prefix "condition-variable-broadcast!"
353
354 (pass-if "condition-variable-broadcast! wakes up multiple threads"
355 (let* ((sem 0)
356 (c1 (make-condition-variable 'condition-variable-broadcast-1-a))
357 (m1 (make-mutex 'condition-variable-broadcast-1-a))
358 (c2 (make-condition-variable 'condition-variable-broadcast-1-b))
359 (m2 (make-mutex 'condition-variable-broadcast-1-b))
360 (inc-sem! (lambda ()
361 (mutex-lock! m1)
362 (set! sem (+ sem 1))
363 (condition-variable-broadcast! c1)
364 (mutex-unlock! m1)))
365 (dec-sem! (lambda ()
366 (mutex-lock! m1)
367 (while (eqv? sem 0) (wait-condition-variable c1 m1))
368 (set! sem (- sem 1))
369 (mutex-unlock! m1)))
370 (t1 (make-thread (lambda ()
371 (mutex-lock! m2)
372 (inc-sem!)
373 (mutex-unlock! m2 c2)
374 (inc-sem!))))
375 (t2 (make-thread (lambda ()
376 (mutex-lock! m2)
377 (inc-sem!)
378 (mutex-unlock! m2 c2)
379 (inc-sem!)))))
380 (thread-start! t1)
381 (thread-start! t2)
382 (dec-sem!)
383 (dec-sem!)
384 (mutex-lock! m2)
385 (condition-variable-broadcast! c2)
386 (mutex-unlock! m2)
387 (dec-sem!)
388 (dec-sem!))))
389
390(with-test-prefix "time?"
391
392 (pass-if "current-time is time" (time? (current-time)))
393 (pass-if "number is not time" (not (time? 123)))
394 (pass-if "symbol not time" (not (time? 'foo))))
395
396(with-test-prefix "time->seconds"
397
398 (pass-if "time->seconds makes time into rational"
399 (rational? (time->seconds (current-time))))
400
401 (pass-if "time->seconds is reversible"
402 (let ((t (current-time)))
403 (equal? t (seconds->time (time->seconds t))))))
404
405(with-test-prefix "seconds->time"
406
407 (pass-if "seconds->time makes rational into time"
408 (time? (seconds->time 123.456)))
409
410 (pass-if "seconds->time is reversible"
411 (let ((t (time->seconds (current-time))))
412 (equal? t (time->seconds (seconds->time t))))))
413
414(with-test-prefix "current-exception-handler"
415
416 (pass-if "current handler returned at top level"
417 (procedure? (current-exception-handler)))
418
419 (pass-if "specified handler set under with-exception-handler"
420 (let ((h (lambda (key . args) 'nothing)))
421 (with-exception-handler h (lambda () (eq? (current-exception-handler)
422 h)))))
423
424 (pass-if "multiple levels of handler nesting"
425 (let ((h (lambda (key . args) 'nothing))
426 (i (current-exception-handler)))
427 (and (with-exception-handler h (lambda ()
428 (eq? (current-exception-handler) h)))
429 (eq? (current-exception-handler) i))))
430
431 (pass-if "exception handler installation is thread-safe"
432 (let* ((h1 (current-exception-handler))
433 (h2 (lambda (key . args) 'nothing-2))
434 (m (make-mutex 'current-exception-handler-4))
435 (c (make-condition-variable 'current-exception-handler-4))
436 (t (make-thread (lambda ()
437 (with-exception-handler
438 h2 (lambda ()
439 (mutex-lock! m)
440 (condition-variable-signal! c)
441 (wait-condition-variable c m)
442 (and (eq? (current-exception-handler) h2)
443 (mutex-unlock! m)))))
444 'current-exception-handler-4)))
445 (mutex-lock! m)
446 (thread-start! t)
447 (wait-condition-variable c m)
448 (and (eq? (current-exception-handler) h1)
449 (condition-variable-signal! c)
450 (mutex-unlock! m)
451 (thread-join! t)))))
452
453(with-test-prefix "uncaught-exception-reason"
454
455 (pass-if "initial handler captures top level exception"
456 (let ((t (make-thread (lambda () (raise 'foo))))
457 (success #f))
458 (thread-start! t)
459 (with-exception-handler
460 (lambda (obj)
461 (and (uncaught-exception? obj)
462 (eq? (uncaught-exception-reason obj) 'foo)
463 (set! success #t)))
464 (lambda () (thread-join! t)))
465 success))
466
467 (pass-if "initial handler captures non-SRFI-18 throw"
468 (let ((t (make-thread (lambda () (throw 'foo))))
469 (success #f))
470 (thread-start! t)
471 (with-exception-handler
472 (lambda (obj)
473 (and (uncaught-exception? obj)
474 (eq? (uncaught-exception-reason obj) 'foo)
475 (set! success #t)))
476 (lambda () (thread-join! t)))
477 success)))