srfi-18.test 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  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 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. (cond
  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 sleeps fractions of a second"
  77. (let* ((current (time->seconds (current-time)))
  78. (future (+ current 0.5)))
  79. (thread-sleep! future)
  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 (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 (current-time) #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 (+ (time->seconds (current-time))
  194. 100)
  195. #f)))))
  196. (mutex-lock! m)
  197. (thread-start! t)
  198. (mutex-unlock! m)
  199. (thread-join! t)))
  200. (pass-if "can lock mutex for non-current thread"
  201. (let* ((m1 (make-mutex 'mutex-lock-4a))
  202. (m2 (make-mutex 'mutex-lock-4b))
  203. (t (make-thread (lambda () (mutex-lock! m1)) 'mutex-lock-4)))
  204. (mutex-lock! m1)
  205. (thread-start! t)
  206. (mutex-lock! m2 #f t)
  207. (let ((success (eq? (mutex-state m2) t)))
  208. (thread-terminate! t) success)))
  209. (pass-if "locking abandoned mutex throws exception"
  210. (let* ((m (make-mutex 'mutex-lock-5))
  211. (t (make-thread (lambda () (mutex-lock! m)) 'mutex-lock-5))
  212. (success #f))
  213. (thread-start! t)
  214. (thread-join! t)
  215. (with-exception-handler
  216. (lambda (obj) (set! success (abandoned-mutex-exception? obj)))
  217. (lambda () (mutex-lock! m)))
  218. (and success (eq? (mutex-state m) (current-thread)))))
  219. (pass-if "sleeping threads notified of abandonment"
  220. (let* ((m1 (make-mutex 'mutex-lock-6a))
  221. (m2 (make-mutex 'mutex-lock-6b))
  222. (c (make-condition-variable 'mutex-lock-6))
  223. (t (make-thread (lambda ()
  224. (mutex-lock! m1)
  225. (mutex-lock! m2)
  226. (condition-variable-signal! c))))
  227. (success #f))
  228. (mutex-lock! m1)
  229. (thread-start! t)
  230. (with-exception-handler
  231. (lambda (obj) (set! success (abandoned-mutex-exception? obj)))
  232. (lambda () (mutex-unlock! m1 c) (mutex-lock! m2)))
  233. success)))
  234. (with-test-prefix "mutex-unlock!"
  235. (pass-if "unlock changes mutex state"
  236. (let* ((m (make-mutex 'mutex-unlock-1)))
  237. (mutex-lock! m)
  238. (mutex-unlock! m)
  239. (eq? (mutex-state m) 'not-abandoned)))
  240. (pass-if "can unlock from any thread"
  241. (let* ((m (make-mutex 'mutex-unlock-2))
  242. (t (make-thread (lambda () (mutex-unlock! m)) 'mutex-unlock-2)))
  243. (mutex-lock! m)
  244. (thread-start! t)
  245. (thread-join! t)
  246. (eq? (mutex-state m) 'not-abandoned)))
  247. (pass-if "mutex unlock is true when condition is signalled"
  248. (let* ((m (make-mutex 'mutex-unlock-3))
  249. (c (make-condition-variable 'mutex-unlock-3))
  250. (t (make-thread (lambda ()
  251. (mutex-lock! m)
  252. (condition-variable-signal! c)
  253. (mutex-unlock! m)))))
  254. (mutex-lock! m)
  255. (thread-start! t)
  256. (mutex-unlock! m c)))
  257. (pass-if "mutex unlock is false when condition times out"
  258. (let* ((m (make-mutex 'mutex-unlock-4))
  259. (c (make-condition-variable 'mutex-unlock-4)))
  260. (mutex-lock! m)
  261. (not (mutex-unlock! m c (+ (time->seconds (current-time)) 1))))))
  262. (with-test-prefix "condition-variable?"
  263. (pass-if "make-condition-variable creates condition variable"
  264. (condition-variable? (make-condition-variable)))
  265. (pass-if "symbol not condition variable"
  266. (not (condition-variable? 'foo))))
  267. (with-test-prefix "condition-variable-name"
  268. (pass-if "make-condition-variable with name binds name"
  269. (let* ((c (make-condition-variable 'condition-variable-name-1)))
  270. (eq? (condition-variable-name c) 'condition-variable-name-1)))
  271. (pass-if "make-condition-variable without name does not bind name"
  272. (let* ((c (make-condition-variable)))
  273. (not (condition-variable-name c)))))
  274. (with-test-prefix "condition-variable-specific"
  275. (pass-if "condition-variable-specific is initially #f"
  276. (let ((c (make-condition-variable 'condition-variable-specific-1)))
  277. (not (condition-variable-specific c))))
  278. (pass-if "condition-variable-specific-set! can set value"
  279. (let ((c (make-condition-variable 'condition-variable-specific-1)))
  280. (condition-variable-specific-set! c "hello")
  281. (equal? (condition-variable-specific c) "hello"))))
  282. (with-test-prefix "condition-variable-signal!"
  283. (pass-if "condition-variable-signal! wakes up single thread"
  284. (let* ((m (make-mutex 'condition-variable-signal-1))
  285. (c (make-condition-variable 'condition-variable-signal-1))
  286. (t (make-thread (lambda ()
  287. (mutex-lock! m)
  288. (condition-variable-signal! c)
  289. (mutex-unlock! m)))))
  290. (mutex-lock! m)
  291. (thread-start! t)
  292. (mutex-unlock! m c))))
  293. (with-test-prefix "condition-variable-broadcast!"
  294. (pass-if "condition-variable-broadcast! wakes up multiple threads"
  295. (let* ((sem 0)
  296. (c1 (make-condition-variable 'condition-variable-broadcast-1-a))
  297. (m1 (make-mutex 'condition-variable-broadcast-1-a))
  298. (c2 (make-condition-variable 'condition-variable-broadcast-1-b))
  299. (m2 (make-mutex 'condition-variable-broadcast-1-b))
  300. (inc-sem! (lambda ()
  301. (mutex-lock! m1)
  302. (set! sem (+ sem 1))
  303. (condition-variable-broadcast! c1)
  304. (mutex-unlock! m1)))
  305. (dec-sem! (lambda ()
  306. (mutex-lock! m1)
  307. (while (eqv? sem 0) (wait-condition-variable c1 m1))
  308. (set! sem (- sem 1))
  309. (mutex-unlock! m1)))
  310. (t1 (make-thread (lambda ()
  311. (mutex-lock! m2)
  312. (inc-sem!)
  313. (mutex-unlock! m2 c2)
  314. (inc-sem!))))
  315. (t2 (make-thread (lambda ()
  316. (mutex-lock! m2)
  317. (inc-sem!)
  318. (mutex-unlock! m2 c2)
  319. (inc-sem!)))))
  320. (thread-start! t1)
  321. (thread-start! t2)
  322. (dec-sem!)
  323. (dec-sem!)
  324. (mutex-lock! m2)
  325. (condition-variable-broadcast! c2)
  326. (mutex-unlock! m2)
  327. (dec-sem!)
  328. (dec-sem!))))
  329. (with-test-prefix "time?"
  330. (pass-if "current-time is time" (time? (current-time)))
  331. (pass-if "number is not time" (not (time? 123)))
  332. (pass-if "symbol not time" (not (time? 'foo))))
  333. (with-test-prefix "time->seconds"
  334. (pass-if "time->seconds makes time into rational"
  335. (rational? (time->seconds (current-time))))
  336. (pass-if "time->seconds is reversible"
  337. (let ((t (current-time)))
  338. (equal? t (seconds->time (time->seconds t))))))
  339. (with-test-prefix "seconds->time"
  340. (pass-if "seconds->time makes rational into time"
  341. (time? (seconds->time 123.456)))
  342. (pass-if "seconds->time is reversible"
  343. (let ((t (time->seconds (current-time))))
  344. (equal? t (time->seconds (seconds->time t))))))
  345. (with-test-prefix "current-exception-handler"
  346. (pass-if "current handler returned at top level"
  347. (procedure? (current-exception-handler)))
  348. (pass-if "specified handler set under with-exception-handler"
  349. (let ((h (lambda (key . args) 'nothing)))
  350. (with-exception-handler h (lambda () (eq? (current-exception-handler)
  351. h)))))
  352. (pass-if "multiple levels of handler nesting"
  353. (let ((h (lambda (key . args) 'nothing))
  354. (i (current-exception-handler)))
  355. (and (with-exception-handler h (lambda ()
  356. (eq? (current-exception-handler) h)))
  357. (eq? (current-exception-handler) i))))
  358. (pass-if "exception handler installation is thread-safe"
  359. (let* ((h1 (current-exception-handler))
  360. (h2 (lambda (key . args) 'nothing-2))
  361. (m (make-mutex 'current-exception-handler-4))
  362. (c (make-condition-variable 'current-exception-handler-4))
  363. (t (make-thread (lambda ()
  364. (with-exception-handler
  365. h2 (lambda ()
  366. (mutex-lock! m)
  367. (condition-variable-signal! c)
  368. (wait-condition-variable c m)
  369. (and (eq? (current-exception-handler) h2)
  370. (mutex-unlock! m)))))
  371. 'current-exception-handler-4)))
  372. (mutex-lock! m)
  373. (thread-start! t)
  374. (wait-condition-variable c m)
  375. (and (eq? (current-exception-handler) h1)
  376. (condition-variable-signal! c)
  377. (mutex-unlock! m)
  378. (thread-join! t)))))
  379. (with-test-prefix "uncaught-exception-reason"
  380. (pass-if "initial handler captures top level exception"
  381. (let ((t (make-thread (lambda () (raise 'foo))))
  382. (success #f))
  383. (thread-start! t)
  384. (with-exception-handler
  385. (lambda (obj)
  386. (and (uncaught-exception? obj)
  387. (eq? (uncaught-exception-reason obj) 'foo)
  388. (set! success #t)))
  389. (lambda () (thread-join! t)))
  390. success))
  391. (pass-if "initial handler captures non-SRFI-18 throw"
  392. (let ((t (make-thread (lambda () (throw 'foo))))
  393. (success #f))
  394. (thread-start! t)
  395. (with-exception-handler
  396. (lambda (obj)
  397. (and (uncaught-exception? obj)
  398. (eq? (uncaught-exception-reason obj) 'foo)
  399. (set! success #t)))
  400. (lambda () (thread-join! t)))
  401. success)))))