threads.test 12 KB

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