threads.scm 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. ;;;; Copyright (C) 1996, 1998, 2001, 2002, 2003, 2006, 2010, 2011,
  2. ;;;; 2012 Free Software Foundation, Inc.
  3. ;;;;
  4. ;;;; This library is free software; you can redistribute it and/or
  5. ;;;; modify it under the terms of the GNU Lesser General Public
  6. ;;;; License as published by the Free Software Foundation; either
  7. ;;;; version 3 of the License, or (at your option) any later version.
  8. ;;;;
  9. ;;;; This library is distributed in the hope that it will be useful,
  10. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;;; Lesser General Public License for more details.
  13. ;;;;
  14. ;;;; You should have received a copy of the GNU Lesser General Public
  15. ;;;; License along with this library; if not, write to the Free Software
  16. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. ;;;;
  18. ;;;; ----------------------------------------------------------------
  19. ;;;; threads.scm -- User-level interface to Guile's thread system
  20. ;;;; 4 March 1996, Anthony Green <green@cygnus.com>
  21. ;;;; Modified 5 October 1996, MDJ <djurfeldt@nada.kth.se>
  22. ;;;; Modified 6 April 2001, ttn
  23. ;;;; ----------------------------------------------------------------
  24. ;;;;
  25. ;;; Commentary:
  26. ;; This module is documented in the Guile Reference Manual.
  27. ;; Briefly, one procedure is exported: `%thread-handler';
  28. ;; as well as four macros: `make-thread', `begin-thread',
  29. ;; `with-mutex' and `monitor'.
  30. ;;; Code:
  31. (define-module (ice-9 threads)
  32. #:use-module (ice-9 futures)
  33. #:use-module (ice-9 match)
  34. #:export (begin-thread
  35. parallel
  36. letpar
  37. make-thread
  38. with-mutex
  39. monitor
  40. par-map
  41. par-for-each
  42. n-par-map
  43. n-par-for-each
  44. n-for-each-par-map
  45. %thread-handler))
  46. ;;; Macros first, so that the procedures expand correctly.
  47. (define-syntax-rule (begin-thread e0 e1 ...)
  48. (call-with-new-thread
  49. (lambda () e0 e1 ...)
  50. %thread-handler))
  51. (define-syntax parallel
  52. (lambda (x)
  53. (syntax-case x ()
  54. ((_ e0 ...)
  55. (with-syntax (((tmp0 ...) (generate-temporaries (syntax (e0 ...)))))
  56. #'(let ((tmp0 (future e0))
  57. ...)
  58. (values (touch tmp0) ...)))))))
  59. (define-syntax-rule (letpar ((v e) ...) b0 b1 ...)
  60. (call-with-values
  61. (lambda () (parallel e ...))
  62. (lambda (v ...)
  63. b0 b1 ...)))
  64. (define-syntax-rule (make-thread proc arg ...)
  65. (call-with-new-thread
  66. (lambda () (proc arg ...))
  67. %thread-handler))
  68. (define-syntax-rule (with-mutex m e0 e1 ...)
  69. (let ((x m))
  70. (dynamic-wind
  71. (lambda () (lock-mutex x))
  72. (lambda () (begin e0 e1 ...))
  73. (lambda () (unlock-mutex x)))))
  74. (define-syntax-rule (monitor first rest ...)
  75. (with-mutex (make-mutex)
  76. first rest ...))
  77. (define (par-mapper mapper cons)
  78. (lambda (proc . lists)
  79. (let loop ((lists lists))
  80. (match lists
  81. (((heads tails ...) ...)
  82. (let ((tail (future (loop tails)))
  83. (head (apply proc heads)))
  84. (cons head (touch tail))))
  85. (_
  86. '())))))
  87. (define par-map (par-mapper map cons))
  88. (define par-for-each (par-mapper for-each (const *unspecified*)))
  89. (define (n-par-map n proc . arglists)
  90. (let* ((m (make-mutex))
  91. (threads '())
  92. (results (make-list (length (car arglists))))
  93. (result results))
  94. (do ((i 0 (+ 1 i)))
  95. ((= i n)
  96. (for-each join-thread threads)
  97. results)
  98. (set! threads
  99. (cons (begin-thread
  100. (let loop ()
  101. (lock-mutex m)
  102. (if (null? result)
  103. (unlock-mutex m)
  104. (let ((args (map car arglists))
  105. (my-result result))
  106. (set! arglists (map cdr arglists))
  107. (set! result (cdr result))
  108. (unlock-mutex m)
  109. (set-car! my-result (apply proc args))
  110. (loop)))))
  111. threads)))))
  112. (define (n-par-for-each n proc . arglists)
  113. (let ((m (make-mutex))
  114. (threads '()))
  115. (do ((i 0 (+ 1 i)))
  116. ((= i n)
  117. (for-each join-thread threads))
  118. (set! threads
  119. (cons (begin-thread
  120. (let loop ()
  121. (lock-mutex m)
  122. (if (null? (car arglists))
  123. (unlock-mutex m)
  124. (let ((args (map car arglists)))
  125. (set! arglists (map cdr arglists))
  126. (unlock-mutex m)
  127. (apply proc args)
  128. (loop)))))
  129. threads)))))
  130. ;;; The following procedure is motivated by the common and important
  131. ;;; case where a lot of work should be done, (not too much) in parallel,
  132. ;;; but the results need to be handled serially (for example when
  133. ;;; writing them to a file).
  134. ;;;
  135. (define (n-for-each-par-map n s-proc p-proc . arglists)
  136. "Using N parallel processes, apply S-PROC in serial order on the results
  137. of applying P-PROC on ARGLISTS."
  138. (let* ((m (make-mutex))
  139. (threads '())
  140. (no-result '(no-value))
  141. (results (make-list (length (car arglists)) no-result))
  142. (result results))
  143. (do ((i 0 (+ 1 i)))
  144. ((= i n)
  145. (for-each join-thread threads))
  146. (set! threads
  147. (cons (begin-thread
  148. (let loop ()
  149. (lock-mutex m)
  150. (cond ((null? results)
  151. (unlock-mutex m))
  152. ((not (eq? (car results) no-result))
  153. (let ((arg (car results)))
  154. ;; stop others from choosing to process results
  155. (set-car! results no-result)
  156. (unlock-mutex m)
  157. (s-proc arg)
  158. (lock-mutex m)
  159. (set! results (cdr results))
  160. (unlock-mutex m)
  161. (loop)))
  162. ((null? result)
  163. (unlock-mutex m))
  164. (else
  165. (let ((args (map car arglists))
  166. (my-result result))
  167. (set! arglists (map cdr arglists))
  168. (set! result (cdr result))
  169. (unlock-mutex m)
  170. (set-car! my-result (apply p-proc args))
  171. (loop))))))
  172. threads)))))
  173. (define (thread-handler tag . args)
  174. (let ((n (length args))
  175. (p (current-error-port)))
  176. (display "In thread:" p)
  177. (newline p)
  178. (if (>= n 3)
  179. (display-error #f
  180. p
  181. (car args)
  182. (cadr args)
  183. (caddr args)
  184. (if (= n 4)
  185. (cadddr args)
  186. '()))
  187. (begin
  188. (display "uncaught throw to " p)
  189. (display tag p)
  190. (display ": " p)
  191. (display args p)
  192. (newline p)))
  193. #f))
  194. ;;; Set system thread handler
  195. (define %thread-handler thread-handler)
  196. ;;; threads.scm ends here