srfi-18.test 19 KB

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