futures.scm 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. ;;; -*- mode: scheme; coding: utf-8; -*-
  2. ;;;
  3. ;;; Copyright (C) 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 (ice-9 futures)
  19. #:use-module (srfi srfi-1)
  20. #:use-module (srfi srfi-9)
  21. #:use-module (ice-9 q)
  22. #:export (future make-future future? touch))
  23. ;;; Author: Ludovic Courtès <ludo@gnu.org>
  24. ;;;
  25. ;;; Commentary:
  26. ;;;
  27. ;;; This module provides an implementation of futures, a mechanism for
  28. ;;; fine-grain parallelism. Futures were first described by Henry Baker
  29. ;;; in ``The Incremental Garbage Collection of Processes'', 1977, and
  30. ;;; then implemented in MultiLisp (an implicit variant thereof, i.e.,
  31. ;;; without `touch'.)
  32. ;;;
  33. ;;; This modules uses a fixed thread pool, normally one per CPU core.
  34. ;;; Futures are off-loaded to these threads, when they are idle.
  35. ;;;
  36. ;;; Code:
  37. ;;;
  38. ;;; Futures.
  39. ;;;
  40. (define-record-type <future>
  41. (%make-future thunk done? mutex)
  42. future?
  43. (thunk future-thunk)
  44. (done? future-done? set-future-done?!)
  45. (result future-result set-future-result!)
  46. (mutex future-mutex))
  47. (define (make-future thunk)
  48. "Return a new future for THUNK. Execution may start at any point
  49. concurrently, or it can start at the time when the returned future is
  50. touched."
  51. (create-workers!)
  52. (let ((future (%make-future thunk #f (make-mutex))))
  53. (register-future! future)
  54. future))
  55. ;;;
  56. ;;; Future queues.
  57. ;;;
  58. (define %futures (make-q))
  59. (define %futures-mutex (make-mutex))
  60. (define %futures-available (make-condition-variable))
  61. (define (register-future! future)
  62. ;; Register FUTURE as being processable.
  63. (lock-mutex %futures-mutex)
  64. (enq! %futures future)
  65. (signal-condition-variable %futures-available)
  66. (unlock-mutex %futures-mutex))
  67. (define (process-future! future)
  68. ;; Process FUTURE, assuming its mutex is already taken.
  69. (set-future-result! future
  70. (catch #t
  71. (lambda ()
  72. (call-with-values (future-thunk future)
  73. (lambda results
  74. (lambda ()
  75. (apply values results)))))
  76. (lambda args
  77. (lambda ()
  78. (apply throw args)))))
  79. (set-future-done?! future #t))
  80. (define (process-futures)
  81. ;; Wait for futures to be available and process them.
  82. (lock-mutex %futures-mutex)
  83. (let loop ()
  84. (wait-condition-variable %futures-available
  85. %futures-mutex)
  86. (or (q-empty? %futures)
  87. (let ((future (deq! %futures)))
  88. (lock-mutex (future-mutex future))
  89. (or (and (future-done? future)
  90. (unlock-mutex (future-mutex future)))
  91. (begin
  92. ;; Do the actual work.
  93. ;; We want to release %FUTURES-MUTEX so that other workers
  94. ;; can progress. However, to avoid deadlocks, we have to
  95. ;; unlock FUTURE as well, to preserve lock ordering.
  96. (unlock-mutex (future-mutex future))
  97. (unlock-mutex %futures-mutex)
  98. (lock-mutex (future-mutex future))
  99. (or (future-done? future) ; lost the race?
  100. (process-future! future))
  101. (unlock-mutex (future-mutex future))
  102. (lock-mutex %futures-mutex)))))
  103. (loop)))
  104. (define (touch future)
  105. "Return the result of FUTURE, computing it if not already done."
  106. (lock-mutex (future-mutex future))
  107. (or (future-done? future)
  108. (begin
  109. ;; Do the actual work. Unlock FUTURE first to preserve lock
  110. ;; ordering.
  111. (unlock-mutex (future-mutex future))
  112. (lock-mutex %futures-mutex)
  113. (q-remove! %futures future)
  114. (unlock-mutex %futures-mutex)
  115. (lock-mutex (future-mutex future))
  116. (or (future-done? future) ; lost the race?
  117. (process-future! future))))
  118. (unlock-mutex (future-mutex future))
  119. ((future-result future)))
  120. ;;;
  121. ;;; Workers.
  122. ;;;
  123. (define %worker-count
  124. (if (provided? 'threads)
  125. (- (current-processor-count) 1)
  126. 0))
  127. ;; A dock of workers that stay here forever.
  128. ;; TODO
  129. ;; 1. Allow the pool to be shrunk, as in libgomp (though that we'd
  130. ;; need semaphores, which aren't yet in libguile!).
  131. ;; 2. Provide a `worker-count' fluid.
  132. (define %workers '())
  133. (define (%create-workers!)
  134. (lock-mutex %futures-mutex)
  135. (set! %workers
  136. (unfold (lambda (i) (>= i %worker-count))
  137. (lambda (i)
  138. (call-with-new-thread process-futures))
  139. 1+
  140. 0))
  141. (set! create-workers! (lambda () #t))
  142. (unlock-mutex %futures-mutex))
  143. (define create-workers!
  144. (lambda () (%create-workers!)))
  145. ;;;
  146. ;;; Syntax.
  147. ;;;
  148. (define-syntax-rule (future body)
  149. "Return a new future for BODY."
  150. (make-future (lambda () body)))