threads.test 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. ;;;; threads.test --- Tests for Guile threading. -*- scheme -*-
  2. ;;;;
  3. ;;;; Copyright 2003, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
  4. ;;;;
  5. ;;;; This library is free software; you can redistribute it and/or
  6. ;;;; modify it under the terms of the GNU Lesser General Public
  7. ;;;; License as published by the Free Software Foundation; either
  8. ;;;; version 3 of the License, or (at your option) any later version.
  9. ;;;;
  10. ;;;; This library is distributed in the hope that it will be useful,
  11. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;;; Lesser General Public License for more details.
  14. ;;;;
  15. ;;;; You should have received a copy of the GNU Lesser General Public
  16. ;;;; License along with this library; if not, write to the Free Software
  17. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. (define-module (test-threads)
  19. #:use-module (ice-9 threads)
  20. #:use-module (system base compile)
  21. #:use-module (test-suite lib))
  22. (define (asyncs-still-working?)
  23. (let ((a #f))
  24. (system-async-mark (lambda ()
  25. (set! a #t)))
  26. ;; The point of the following (equal? ...) is to go through
  27. ;; primitive code (scm_equal_p) that includes a SCM_TICK call and
  28. ;; hence gives system asyncs a chance to run. Of course the
  29. ;; evaluator (eval.i.c) also calls SCM_TICK regularly, but in the
  30. ;; near future we may be using the VM instead of the traditional
  31. ;; compiler, and then we will still want asyncs-still-working? to
  32. ;; work. (The VM should probably have SCM_TICK calls too, but
  33. ;; let's not rely on that here.)
  34. (equal? '(a b c) '(a b c))
  35. a))
  36. (if (provided? 'threads)
  37. (begin
  38. (with-test-prefix "parallel"
  39. (pass-if "no forms"
  40. (call-with-values
  41. (lambda ()
  42. (parallel))
  43. (lambda ()
  44. #t)))
  45. (pass-if "1"
  46. (call-with-values
  47. (lambda ()
  48. (parallel 1))
  49. (lambda (x)
  50. (equal? x 1))))
  51. (pass-if "1 2"
  52. (call-with-values
  53. (lambda ()
  54. (parallel 1 2))
  55. (lambda (x y)
  56. (and (equal? x 1)
  57. (equal? y 2)))))
  58. (pass-if "1 2 3"
  59. (call-with-values
  60. (lambda ()
  61. (parallel 1 2 3))
  62. (lambda (x y z)
  63. (and (equal? x 1)
  64. (equal? y 2)
  65. (equal? z 3))))))
  66. ;;
  67. ;; par-map
  68. ;;
  69. (with-test-prefix "par-map"
  70. (pass-if "simple"
  71. (compile '(letrec ((fibo (lambda (n)
  72. (if (<= n 1)
  73. n
  74. (+ (fibo (- n 1))
  75. (fibo (- n 2)))))))
  76. (equal? (par-map fibo (iota 13))
  77. (map fibo (iota 13))))
  78. #:to 'value
  79. #:env (current-module))))
  80. ;;
  81. ;; par-for-each
  82. ;;
  83. (with-test-prefix "par-for-each"
  84. (pass-if "simple"
  85. (compile '(let ((v (make-vector 6 #f)))
  86. (par-for-each (lambda (n)
  87. (vector-set! v n n))
  88. (iota 6))
  89. (equal? v (list->vector (iota 6))))
  90. #:to 'value
  91. #:env (current-module))))
  92. ;;
  93. ;; n-par-for-each
  94. ;;
  95. (with-test-prefix "n-par-for-each"
  96. (pass-if "0 in limit 10"
  97. (n-par-for-each 10 noop '())
  98. #t)
  99. (pass-if "6 in limit 10"
  100. (let ((v (make-vector 6 #f)))
  101. (n-par-for-each 10 (lambda (n)
  102. (vector-set! v n #t))
  103. '(0 1 2 3 4 5))
  104. (equal? v '#(#t #t #t #t #t #t))))
  105. (pass-if "6 in limit 1"
  106. (let ((v (make-vector 6 #f)))
  107. (n-par-for-each 1 (lambda (n)
  108. (vector-set! v n #t))
  109. '(0 1 2 3 4 5))
  110. (equal? v '#(#t #t #t #t #t #t))))
  111. (pass-if "6 in limit 2"
  112. (let ((v (make-vector 6 #f)))
  113. (n-par-for-each 2 (lambda (n)
  114. (vector-set! v n #t))
  115. '(0 1 2 3 4 5))
  116. (equal? v '#(#t #t #t #t #t #t))))
  117. (pass-if "6 in limit 3"
  118. (let ((v (make-vector 6 #f)))
  119. (n-par-for-each 3 (lambda (n)
  120. (vector-set! v n #t))
  121. '(0 1 2 3 4 5))
  122. (equal? v '#(#t #t #t #t #t #t)))))
  123. ;;
  124. ;; n-for-each-par-map
  125. ;;
  126. (with-test-prefix "n-for-each-par-map"
  127. (pass-if "asyncs are still working 2"
  128. (asyncs-still-working?))
  129. (pass-if "0 in limit 10"
  130. (n-for-each-par-map 10 noop noop '())
  131. #t)
  132. (pass-if "6 in limit 10"
  133. (let ((result '()))
  134. (n-for-each-par-map 10
  135. (lambda (n) (set! result (cons n result)))
  136. (lambda (n) (* 2 n))
  137. '(0 1 2 3 4 5))
  138. (equal? result '(10 8 6 4 2 0))))
  139. (pass-if "6 in limit 1"
  140. (let ((result '()))
  141. (n-for-each-par-map 1
  142. (lambda (n) (set! result (cons n result)))
  143. (lambda (n) (* 2 n))
  144. '(0 1 2 3 4 5))
  145. (equal? result '(10 8 6 4 2 0))))
  146. (pass-if "6 in limit 2"
  147. (let ((result '()))
  148. (n-for-each-par-map 2
  149. (lambda (n) (set! result (cons n result)))
  150. (lambda (n) (* 2 n))
  151. '(0 1 2 3 4 5))
  152. (equal? result '(10 8 6 4 2 0))))
  153. (pass-if "6 in limit 3"
  154. (let ((result '()))
  155. (n-for-each-par-map 3
  156. (lambda (n) (set! result (cons n result)))
  157. (lambda (n) (* 2 n))
  158. '(0 1 2 3 4 5))
  159. (equal? result '(10 8 6 4 2 0)))))
  160. ;;
  161. ;; timed mutex locking
  162. ;;
  163. (with-test-prefix "lock-mutex"
  164. (pass-if "asyncs are still working 3"
  165. (asyncs-still-working?))
  166. (pass-if "timed locking fails if timeout exceeded"
  167. (let ((m (make-mutex)))
  168. (lock-mutex m)
  169. (let ((t (begin-thread (lock-mutex m (+ (current-time) 1)))))
  170. (not (join-thread t)))))
  171. (pass-if "asyncs are still working 6"
  172. (asyncs-still-working?))
  173. (pass-if "timed locking succeeds if mutex unlocked within timeout"
  174. (let* ((m (make-mutex))
  175. (c (make-condition-variable))
  176. (cm (make-mutex)))
  177. (lock-mutex cm)
  178. (let ((t (begin-thread (begin (lock-mutex cm)
  179. (signal-condition-variable c)
  180. (unlock-mutex cm)
  181. (lock-mutex m
  182. (+ (current-time) 5))))))
  183. (lock-mutex m)
  184. (wait-condition-variable c cm)
  185. (unlock-mutex cm)
  186. (sleep 1)
  187. (unlock-mutex m)
  188. (join-thread t))))
  189. (pass-if "asyncs are still working 7"
  190. (asyncs-still-working?))
  191. )
  192. ;;
  193. ;; timed mutex unlocking
  194. ;;
  195. (with-test-prefix "unlock-mutex"
  196. (pass-if "asyncs are still working 5"
  197. (asyncs-still-working?))
  198. (pass-if "timed unlocking returns #f if timeout exceeded"
  199. (let ((m (make-mutex))
  200. (c (make-condition-variable)))
  201. (lock-mutex m)
  202. (not (unlock-mutex m c (current-time)))))
  203. (pass-if "asyncs are still working 4"
  204. (asyncs-still-working?))
  205. (pass-if "timed unlocking returns #t if condition signaled"
  206. (let ((m1 (make-mutex))
  207. (m2 (make-mutex))
  208. (c1 (make-condition-variable))
  209. (c2 (make-condition-variable)))
  210. (lock-mutex m1)
  211. (let ((t (begin-thread (begin (lock-mutex m1)
  212. (signal-condition-variable c1)
  213. (lock-mutex m2)
  214. (unlock-mutex m1)
  215. (unlock-mutex m2
  216. c2
  217. (+ (current-time)
  218. 5))))))
  219. (wait-condition-variable c1 m1)
  220. (unlock-mutex m1)
  221. (lock-mutex m2)
  222. (signal-condition-variable c2)
  223. (unlock-mutex m2)
  224. (join-thread t)))))
  225. ;;
  226. ;; timed joining
  227. ;;
  228. (with-test-prefix "join-thread"
  229. (pass-if "timed joining fails if timeout exceeded"
  230. (let* ((m (make-mutex))
  231. (c (make-condition-variable))
  232. (t (begin-thread (begin (lock-mutex m)
  233. (wait-condition-variable c m))))
  234. (r (join-thread t (current-time))))
  235. (cancel-thread t)
  236. (not r)))
  237. (pass-if "join-thread returns timeoutval on timeout"
  238. (let* ((m (make-mutex))
  239. (c (make-condition-variable))
  240. (t (begin-thread (begin (lock-mutex m)
  241. (wait-condition-variable c m))))
  242. (r (join-thread t (current-time) 'foo)))
  243. (cancel-thread t)
  244. (eq? r 'foo)))
  245. (pass-if "timed joining succeeds if thread exits within timeout"
  246. (let ((t (begin-thread (begin (sleep 1) #t))))
  247. (join-thread t (+ (current-time) 5))))
  248. (pass-if "asyncs are still working 1"
  249. (asyncs-still-working?))
  250. ;; scm_join_thread_timed has a SCM_TICK in the middle of it,
  251. ;; to allow asyncs to run (including signal delivery). We
  252. ;; used to have a bug whereby if the joined thread terminated
  253. ;; at the same time as the joining thread is in this SCM_TICK,
  254. ;; scm_join_thread_timed would not notice and would hang
  255. ;; forever. So in this test we are setting up the following
  256. ;; sequence of events.
  257. ;; T=0 other thread is created and starts running
  258. ;; T=2 main thread sets up an async that will sleep for 10 seconds
  259. ;; T=2 main thread calls join-thread, which will...
  260. ;; T=2 ...call the async, which starts sleeping
  261. ;; T=5 other thread finishes its work and terminates
  262. ;; T=7 async completes, main thread continues inside join-thread.
  263. (pass-if "don't hang when joined thread terminates in SCM_TICK"
  264. (let ((other-thread (make-thread sleep 5)))
  265. (letrec ((delay-count 10)
  266. (aproc (lambda ()
  267. (set! delay-count (- delay-count 1))
  268. (if (zero? delay-count)
  269. (sleep 5)
  270. (system-async-mark aproc)))))
  271. (sleep 2)
  272. (system-async-mark aproc)
  273. (join-thread other-thread)))
  274. #t))
  275. ;;
  276. ;; thread cancellation
  277. ;;
  278. (with-test-prefix "cancel-thread"
  279. (pass-if "cancel succeeds"
  280. (let ((m (make-mutex)))
  281. (lock-mutex m)
  282. (let ((t (begin-thread (begin (lock-mutex m) 'foo))))
  283. (cancel-thread t)
  284. (join-thread t)
  285. #t)))
  286. (pass-if "handler result passed to join"
  287. (let ((m (make-mutex)))
  288. (lock-mutex m)
  289. (let ((t (begin-thread (lock-mutex m))))
  290. (set-thread-cleanup! t (lambda () 'foo))
  291. (cancel-thread t)
  292. (eq? (join-thread t) 'foo))))
  293. (pass-if "can cancel self"
  294. (let ((m (make-mutex)))
  295. (lock-mutex m)
  296. (let ((t (begin-thread (begin
  297. (set-thread-cleanup! (current-thread)
  298. (lambda () 'foo))
  299. (cancel-thread (current-thread))
  300. (lock-mutex m)))))
  301. (eq? (join-thread t) 'foo))))
  302. (pass-if "handler supplants final expr"
  303. (let ((t (begin-thread (begin (set-thread-cleanup! (current-thread)
  304. (lambda () 'bar))
  305. 'foo))))
  306. (eq? (join-thread t) 'bar)))
  307. (pass-if "remove handler by setting false"
  308. (let ((m (make-mutex)))
  309. (lock-mutex m)
  310. (let ((t (begin-thread (lock-mutex m) 'bar)))
  311. (set-thread-cleanup! t (lambda () 'foo))
  312. (set-thread-cleanup! t #f)
  313. (unlock-mutex m)
  314. (eq? (join-thread t) 'bar))))
  315. (pass-if "initial handler is false"
  316. (not (thread-cleanup (current-thread)))))
  317. ;;
  318. ;; mutex ownership
  319. ;;
  320. (with-test-prefix "mutex-ownership"
  321. (pass-if "mutex ownership for locked mutex"
  322. (let ((m (make-mutex)))
  323. (lock-mutex m)
  324. (eq? (mutex-owner m) (current-thread))))
  325. (pass-if "mutex ownership for unlocked mutex"
  326. (let ((m (make-mutex)))
  327. (not (mutex-owner m))))
  328. (pass-if "locking mutex on behalf of other thread"
  329. (let* ((m (make-mutex))
  330. (t (begin-thread 'foo)))
  331. (lock-mutex m #f t)
  332. (eq? (mutex-owner m) t)))
  333. (pass-if "locking mutex with no owner"
  334. (let ((m (make-mutex)))
  335. (lock-mutex m #f #f)
  336. (not (mutex-owner m))))
  337. (pass-if "mutex with owner not retained (bug #27450)"
  338. (let ((g (make-guardian)))
  339. (g (let ((m (make-mutex))) (lock-mutex m) m))
  340. ;; Avoid false references to M on the stack.
  341. (let cleanup ((i 20))
  342. (and (> i 0)
  343. (begin (cleanup (1- i)) i)))
  344. (gc) (gc)
  345. (let ((m (g)))
  346. (and (mutex? m)
  347. (eq? (mutex-owner m) (current-thread)))))))
  348. ;;
  349. ;; mutex lock levels
  350. ;;
  351. (with-test-prefix "mutex-lock-levels"
  352. (pass-if "unlocked level is 0"
  353. (let ((m (make-mutex)))
  354. (and (not (mutex-locked? m)) (eqv? (mutex-level m) 0))))
  355. (pass-if "non-recursive lock level is 1"
  356. (let ((m (make-mutex)))
  357. (lock-mutex m)
  358. (and (mutex-locked? m) (eqv? (mutex-level m) 1))))
  359. (pass-if "recursive lock level is >1"
  360. (let ((m (make-mutex 'recursive)))
  361. (lock-mutex m)
  362. (lock-mutex m)
  363. (and (mutex-locked? m) (eqv? (mutex-level m) 2)))))
  364. ;;
  365. ;; mutex behavior
  366. ;;
  367. (with-test-prefix "mutex-behavior"
  368. (pass-if "unchecked unlock"
  369. (let* ((m (make-mutex 'unchecked-unlock)))
  370. (unlock-mutex m)))
  371. (pass-if "allow external unlock"
  372. (let* ((m (make-mutex 'allow-external-unlock))
  373. (t (begin-thread (lock-mutex m))))
  374. (join-thread t)
  375. (unlock-mutex m)))
  376. (pass-if "recursive mutexes"
  377. (let* ((m (make-mutex 'recursive)))
  378. (lock-mutex m)
  379. (lock-mutex m)))
  380. (pass-if "locking abandoned mutex throws exception"
  381. (let* ((m (make-mutex))
  382. (t (begin-thread (lock-mutex m)))
  383. (success #f))
  384. (join-thread t)
  385. (catch 'abandoned-mutex-error
  386. (lambda () (lock-mutex m))
  387. (lambda key (set! success #t)))
  388. success)))))
  389. ;;
  390. ;; nproc
  391. ;;
  392. (with-test-prefix "nproc"
  393. (pass-if "total-processor-count"
  394. (>= (total-processor-count) 1))
  395. (pass-if "current-processor-count"
  396. (and (>= (current-processor-count) 1)
  397. (>= (total-processor-count) (current-processor-count)))))