srfi-18.test 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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 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 (test-suite lib))
  21. ;; two expressions so that the srfi-18 import is in effect for expansion
  22. ;; of the rest
  23. (if (provided? 'threads)
  24. (use-modules (srfi srfi-18)))
  25. (and
  26. (provided? 'threads)
  27. (with-test-prefix "current-thread"
  28. (pass-if "current-thread eq current-thread"
  29. (eq? (current-thread) (current-thread))))
  30. (with-test-prefix "thread?"
  31. (pass-if "current-thread is thread"
  32. (thread? (current-thread)))
  33. (pass-if "foo not thread"
  34. (not (thread? 'foo))))
  35. (with-test-prefix "make-thread"
  36. (pass-if "make-thread creates new thread"
  37. (let* ((n (length (all-threads)))
  38. (t (make-thread (lambda () 'foo) 'make-thread-1))
  39. (r (> (length (all-threads)) n)))
  40. (thread-terminate! t) r)))
  41. (with-test-prefix "thread-name"
  42. (pass-if "make-thread with name binds name"
  43. (let* ((t (make-thread (lambda () 'foo) 'thread-name-1))
  44. (r (eq? (thread-name t) 'thread-name-1)))
  45. (thread-terminate! t) r))
  46. (pass-if "make-thread without name does not bind name"
  47. (let* ((t (make-thread (lambda () 'foo)))
  48. (r (not (thread-name t))))
  49. (thread-terminate! t) r)))
  50. (with-test-prefix "thread-specific"
  51. (pass-if "thread-specific is initially #f"
  52. (let* ((t (make-thread (lambda () 'foo) 'thread-specific-1))
  53. (r (not (thread-specific t))))
  54. (thread-terminate! t) r))
  55. (pass-if "thread-specific-set! can set value"
  56. (let ((t (make-thread (lambda () 'foo) 'thread-specific-2)))
  57. (thread-specific-set! t "hello")
  58. (let ((r (equal? (thread-specific t) "hello")))
  59. (thread-terminate! t) r))))
  60. (with-test-prefix "thread-start!"
  61. (pass-if "thread activates only after start"
  62. (let* ((started #f)
  63. (m (make-mutex 'thread-start-mutex))
  64. (t (make-thread (lambda () (set! started #t)) 'thread-start-1)))
  65. (and (not started) (thread-start! t) (thread-join! t) started))))
  66. (with-test-prefix "thread-yield!"
  67. (pass-if "thread yield suceeds"
  68. (thread-yield!) #t))
  69. (with-test-prefix "thread-sleep!"
  70. (pass-if "thread sleep with time"
  71. (let ((future-time (seconds->time (+ (time->seconds (current-time)) 2))))
  72. (unspecified? (thread-sleep! future-time))))
  73. (pass-if "thread sleep with number"
  74. (let ((old-secs (car (current-time))))
  75. (unspecified? (thread-sleep! (+ (time->seconds (current-time)))))))
  76. (pass-if "thread does not sleep on past time"
  77. (let ((past-time (seconds->time (- (time->seconds (current-time)) 2))))
  78. (unspecified? (thread-sleep! past-time)))))
  79. (with-test-prefix "thread-terminate!"
  80. (pass-if "termination destroys non-started thread"
  81. (let ((t (make-thread (lambda () 'nothing) 'thread-terminate-1))
  82. (num-threads (length (all-threads)))
  83. (success #f))
  84. (thread-terminate! t)
  85. (with-exception-handler
  86. (lambda (obj) (set! success (terminated-thread-exception? obj)))
  87. (lambda () (thread-join! t)))
  88. success))
  89. (pass-if "termination destroys started thread"
  90. (let* ((m1 (make-mutex 'thread-terminate-2a))
  91. (m2 (make-mutex 'thread-terminate-2b))
  92. (c (make-condition-variable 'thread-terminate-2))
  93. (t (make-thread (lambda ()
  94. (mutex-lock! m1)
  95. (condition-variable-signal! c)
  96. (mutex-unlock! m1)
  97. (mutex-lock! m2))
  98. 'thread-terminate-2))
  99. (success #f))
  100. (mutex-lock! m1)
  101. (mutex-lock! m2)
  102. (thread-start! t)
  103. (mutex-unlock! m1 c)
  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. (with-test-prefix "thread-join!"
  110. (pass-if "join receives result of thread"
  111. (let ((t (make-thread (lambda () 'foo) 'thread-join-1)))
  112. (thread-start! t)
  113. (eq? (thread-join! t) 'foo)))
  114. (pass-if "join receives timeout val if timeout expires"
  115. (let* ((m (make-mutex 'thread-join-2))
  116. (t (make-thread (lambda () (mutex-lock! m)) 'thread-join-2)))
  117. (mutex-lock! m)
  118. (thread-start! t)
  119. (let ((r (thread-join! t (current-time) 'bar)))
  120. (thread-terminate! t)
  121. (eq? r 'bar))))
  122. (pass-if "join throws exception on timeout without timeout val"
  123. (let* ((m (make-mutex 'thread-join-3))
  124. (t (make-thread (lambda () (mutex-lock! m)) 'thread-join-3))
  125. (success #f))
  126. (mutex-lock! m)
  127. (thread-start! t)
  128. (with-exception-handler
  129. (lambda (obj) (set! success (join-timeout-exception? obj)))
  130. (lambda () (thread-join! t (current-time))))
  131. (thread-terminate! t)
  132. success))
  133. (pass-if "join waits on timeout"
  134. (let ((t (make-thread (lambda () (sleep 1) 'foo) 'thread-join-4)))
  135. (thread-start! t)
  136. (eq? (thread-join! t (+ (time->seconds (current-time)) 2)) 'foo))))
  137. (with-test-prefix "mutex?"
  138. (pass-if "make-mutex creates mutex"
  139. (mutex? (make-mutex)))
  140. (pass-if "symbol not mutex"
  141. (not (mutex? 'foo))))
  142. (with-test-prefix "mutex-name"
  143. (pass-if "make-mutex with name binds name"
  144. (let* ((m (make-mutex 'mutex-name-1)))
  145. (eq? (mutex-name m) 'mutex-name-1)))
  146. (pass-if "make-mutex without name does not bind name"
  147. (let* ((m (make-mutex)))
  148. (not (mutex-name m)))))
  149. (with-test-prefix "mutex-specific"
  150. (pass-if "mutex-specific is initially #f"
  151. (let ((m (make-mutex 'mutex-specific-1)))
  152. (not (mutex-specific m))))
  153. (pass-if "mutex-specific-set! can set value"
  154. (let ((m (make-mutex 'mutex-specific-2)))
  155. (mutex-specific-set! m "hello")
  156. (equal? (mutex-specific m) "hello"))))
  157. (with-test-prefix "mutex-state"
  158. (pass-if "mutex state is initially not-abandoned"
  159. (let ((m (make-mutex 'mutex-state-1)))
  160. (eq? (mutex-state m) 'not-abandoned)))
  161. (pass-if "mutex state of locked, owned mutex is owner thread"
  162. (let ((m (make-mutex 'mutex-state-2)))
  163. (mutex-lock! m)
  164. (eq? (mutex-state m) (current-thread))))
  165. (pass-if "mutex state of locked, unowned mutex is not-owned"
  166. (let ((m (make-mutex 'mutex-state-3)))
  167. (mutex-lock! m #f #f)
  168. (eq? (mutex-state m) 'not-owned)))
  169. (pass-if "mutex state of unlocked, abandoned mutex is abandoned"
  170. (let* ((m (make-mutex 'mutex-state-4))
  171. (t (make-thread (lambda () (mutex-lock! m)))))
  172. (thread-start! t)
  173. (thread-join! t)
  174. (eq? (mutex-state m) 'abandoned))))
  175. (with-test-prefix "mutex-lock!"
  176. (pass-if "mutex-lock! returns true on successful lock"
  177. (let* ((m (make-mutex 'mutex-lock-1)))
  178. (mutex-lock! m)))
  179. (pass-if "mutex-lock! returns false on timeout"
  180. (let* ((m (make-mutex 'mutex-lock-2))
  181. (t (make-thread (lambda () (mutex-lock! m (current-time) #f)))))
  182. (mutex-lock! m)
  183. (thread-start! t)
  184. (not (thread-join! t))))
  185. (pass-if "mutex-lock! returns true when lock obtained within timeout"
  186. (let* ((m (make-mutex 'mutex-lock-3))
  187. (t (make-thread (lambda ()
  188. (mutex-lock! m (+ (time->seconds (current-time))
  189. 100)
  190. #f)))))
  191. (mutex-lock! m)
  192. (thread-start! t)
  193. (mutex-unlock! m)
  194. (thread-join! t)))
  195. (pass-if "can lock mutex for non-current thread"
  196. (let* ((m1 (make-mutex 'mutex-lock-4a))
  197. (m2 (make-mutex 'mutex-lock-4b))
  198. (t (make-thread (lambda () (mutex-lock! m1)) 'mutex-lock-4)))
  199. (mutex-lock! m1)
  200. (thread-start! t)
  201. (mutex-lock! m2 #f t)
  202. (let ((success (eq? (mutex-state m2) t)))
  203. (thread-terminate! t) success)))
  204. (pass-if "locking abandoned mutex throws exception"
  205. (let* ((m (make-mutex 'mutex-lock-5))
  206. (t (make-thread (lambda () (mutex-lock! m)) 'mutex-lock-5))
  207. (success #f))
  208. (thread-start! t)
  209. (thread-join! t)
  210. (with-exception-handler
  211. (lambda (obj) (set! success (abandoned-mutex-exception? obj)))
  212. (lambda () (mutex-lock! m)))
  213. (and success (eq? (mutex-state m) (current-thread)))))
  214. (pass-if "sleeping threads notified of abandonment"
  215. (let* ((m1 (make-mutex 'mutex-lock-6a))
  216. (m2 (make-mutex 'mutex-lock-6b))
  217. (c (make-condition-variable 'mutex-lock-6))
  218. (t (make-thread (lambda ()
  219. (mutex-lock! m1)
  220. (mutex-lock! m2)
  221. (condition-variable-signal! c))))
  222. (success #f))
  223. (mutex-lock! m1)
  224. (thread-start! t)
  225. (with-exception-handler
  226. (lambda (obj) (set! success (abandoned-mutex-exception? obj)))
  227. (lambda () (mutex-unlock! m1 c) (mutex-lock! m2)))
  228. success)))
  229. (with-test-prefix "mutex-unlock!"
  230. (pass-if "unlock changes mutex state"
  231. (let* ((m (make-mutex 'mutex-unlock-1)))
  232. (mutex-lock! m)
  233. (mutex-unlock! m)
  234. (eq? (mutex-state m) 'not-abandoned)))
  235. (pass-if "can unlock from any thread"
  236. (let* ((m (make-mutex 'mutex-unlock-2))
  237. (t (make-thread (lambda () (mutex-unlock! m)) 'mutex-unlock-2)))
  238. (mutex-lock! m)
  239. (thread-start! t)
  240. (thread-join! t)
  241. (eq? (mutex-state m) 'not-abandoned)))
  242. (pass-if "mutex unlock is true when condition is signalled"
  243. (let* ((m (make-mutex 'mutex-unlock-3))
  244. (c (make-condition-variable 'mutex-unlock-3))
  245. (t (make-thread (lambda ()
  246. (mutex-lock! m)
  247. (condition-variable-signal! c)
  248. (mutex-unlock! m)))))
  249. (mutex-lock! m)
  250. (thread-start! t)
  251. (mutex-unlock! m c)))
  252. (pass-if "mutex unlock is false when condition times out"
  253. (let* ((m (make-mutex 'mutex-unlock-4))
  254. (c (make-condition-variable 'mutex-unlock-4)))
  255. (mutex-lock! m)
  256. (not (mutex-unlock! m c (+ (time->seconds (current-time)) 1))))))
  257. (with-test-prefix "condition-variable?"
  258. (pass-if "make-condition-variable creates condition variable"
  259. (condition-variable? (make-condition-variable)))
  260. (pass-if "symbol not condition variable"
  261. (not (condition-variable? 'foo))))
  262. (with-test-prefix "condition-variable-name"
  263. (pass-if "make-condition-variable with name binds name"
  264. (let* ((c (make-condition-variable 'condition-variable-name-1)))
  265. (eq? (condition-variable-name c) 'condition-variable-name-1)))
  266. (pass-if "make-condition-variable without name does not bind name"
  267. (let* ((c (make-condition-variable)))
  268. (not (condition-variable-name c)))))
  269. (with-test-prefix "condition-variable-specific"
  270. (pass-if "condition-variable-specific is initially #f"
  271. (let ((c (make-condition-variable 'condition-variable-specific-1)))
  272. (not (condition-variable-specific c))))
  273. (pass-if "condition-variable-specific-set! can set value"
  274. (let ((c (make-condition-variable 'condition-variable-specific-1)))
  275. (condition-variable-specific-set! c "hello")
  276. (equal? (condition-variable-specific c) "hello"))))
  277. (with-test-prefix "condition-variable-signal!"
  278. (pass-if "condition-variable-signal! wakes up single thread"
  279. (let* ((m (make-mutex 'condition-variable-signal-1))
  280. (c (make-condition-variable 'condition-variable-signal-1))
  281. (t (make-thread (lambda ()
  282. (mutex-lock! m)
  283. (condition-variable-signal! c)
  284. (mutex-unlock! m)))))
  285. (mutex-lock! m)
  286. (thread-start! t)
  287. (mutex-unlock! m c))))
  288. (with-test-prefix "condition-variable-broadcast!"
  289. (pass-if "condition-variable-broadcast! wakes up multiple threads"
  290. (let* ((sem 0)
  291. (c1 (make-condition-variable 'condition-variable-broadcast-1-a))
  292. (m1 (make-mutex 'condition-variable-broadcast-1-a))
  293. (c2 (make-condition-variable 'condition-variable-broadcast-1-b))
  294. (m2 (make-mutex 'condition-variable-broadcast-1-b))
  295. (inc-sem! (lambda ()
  296. (mutex-lock! m1)
  297. (set! sem (+ sem 1))
  298. (condition-variable-broadcast! c1)
  299. (mutex-unlock! m1)))
  300. (dec-sem! (lambda ()
  301. (mutex-lock! m1)
  302. (while (eqv? sem 0) (wait-condition-variable c1 m1))
  303. (set! sem (- sem 1))
  304. (mutex-unlock! m1)))
  305. (t1 (make-thread (lambda ()
  306. (mutex-lock! m2)
  307. (inc-sem!)
  308. (mutex-unlock! m2 c2)
  309. (inc-sem!))))
  310. (t2 (make-thread (lambda ()
  311. (mutex-lock! m2)
  312. (inc-sem!)
  313. (mutex-unlock! m2 c2)
  314. (inc-sem!)))))
  315. (thread-start! t1)
  316. (thread-start! t2)
  317. (dec-sem!)
  318. (dec-sem!)
  319. (mutex-lock! m2)
  320. (condition-variable-broadcast! c2)
  321. (mutex-unlock! m2)
  322. (dec-sem!)
  323. (dec-sem!))))
  324. (with-test-prefix "time?"
  325. (pass-if "current-time is time" (time? (current-time)))
  326. (pass-if "number is not time" (not (time? 123)))
  327. (pass-if "symbol not time" (not (time? 'foo))))
  328. (with-test-prefix "time->seconds"
  329. (pass-if "time->seconds makes time into rational"
  330. (rational? (time->seconds (current-time))))
  331. (pass-if "time->seconds is reversible"
  332. (let ((t (current-time)))
  333. (equal? t (seconds->time (time->seconds t))))))
  334. (with-test-prefix "seconds->time"
  335. (pass-if "seconds->time makes rational into time"
  336. (time? (seconds->time 123.456)))
  337. (pass-if "seconds->time is reversible"
  338. (let ((t (time->seconds (current-time))))
  339. (equal? t (time->seconds (seconds->time t))))))
  340. (with-test-prefix "current-exception-handler"
  341. (pass-if "current handler returned at top level"
  342. (procedure? (current-exception-handler)))
  343. (pass-if "specified handler set under with-exception-handler"
  344. (let ((h (lambda (key . args) 'nothing)))
  345. (with-exception-handler h (lambda () (eq? (current-exception-handler)
  346. h)))))
  347. (pass-if "multiple levels of handler nesting"
  348. (let ((h (lambda (key . args) 'nothing))
  349. (i (current-exception-handler)))
  350. (and (with-exception-handler h (lambda ()
  351. (eq? (current-exception-handler) h)))
  352. (eq? (current-exception-handler) i))))
  353. (pass-if "exception handler installation is thread-safe"
  354. (let* ((h1 (current-exception-handler))
  355. (h2 (lambda (key . args) 'nothing-2))
  356. (m (make-mutex 'current-exception-handler-4))
  357. (c (make-condition-variable 'current-exception-handler-4))
  358. (t (make-thread (lambda ()
  359. (with-exception-handler
  360. h2 (lambda ()
  361. (mutex-lock! m)
  362. (condition-variable-signal! c)
  363. (wait-condition-variable c m)
  364. (and (eq? (current-exception-handler) h2)
  365. (mutex-unlock! m)))))
  366. 'current-exception-handler-4)))
  367. (mutex-lock! m)
  368. (thread-start! t)
  369. (wait-condition-variable c m)
  370. (and (eq? (current-exception-handler) h1)
  371. (condition-variable-signal! c)
  372. (mutex-unlock! m)
  373. (thread-join! t)))))
  374. (with-test-prefix "uncaught-exception-reason"
  375. (pass-if "initial handler captures top level exception"
  376. (let ((t (make-thread (lambda () (raise 'foo))))
  377. (success #f))
  378. (thread-start! t)
  379. (with-exception-handler
  380. (lambda (obj)
  381. (and (uncaught-exception? obj)
  382. (eq? (uncaught-exception-reason obj) 'foo)
  383. (set! success #t)))
  384. (lambda () (thread-join! t)))
  385. success))
  386. (pass-if "initial handler captures non-SRFI-18 throw"
  387. (let ((t (make-thread (lambda () (throw 'foo))))
  388. (success #f))
  389. (thread-start! t)
  390. (with-exception-handler
  391. (lambda (obj)
  392. (and (uncaught-exception? obj)
  393. (eq? (uncaught-exception-reason obj) 'foo)
  394. (set! success #t)))
  395. (lambda () (thread-join! t)))
  396. success)))
  397. )