]> git.donarmstrong.com Git - lilypond.git/blobdiff - guile18/ice-9/occam-channel.scm
Import guile-1.8 as multiple upstream tarball component
[lilypond.git] / guile18 / ice-9 / occam-channel.scm
diff --git a/guile18/ice-9/occam-channel.scm b/guile18/ice-9/occam-channel.scm
new file mode 100644 (file)
index 0000000..e28f73d
--- /dev/null
@@ -0,0 +1,262 @@
+;;;; Occam-like channels
+
+;;; Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+;;;
+;; This library is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU Lesser General Public
+;; License as published by the Free Software Foundation; either
+;; version 2.1 of the License, or (at your option) any later version.
+;; 
+;; This library is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; Lesser General Public License for more details.
+;; 
+;; You should have received a copy of the GNU Lesser General Public
+;; License along with this library; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+(define-module (ice-9 occam-channel)
+  #:use-syntax (ice-9 syncase)
+  #:use-module (oop goops)
+  #:use-module (ice-9 threads)
+  #:export-syntax (alt
+                  ;; macro use:
+                  oc:lock oc:unlock oc:consequence
+                  oc:immediate-dispatch oc:late-dispatch oc:first-channel
+                  oc:set-handshake-channel oc:unset-handshake-channel)
+  #:export (make-channel
+           ?
+           !
+           make-timer
+           ;; macro use:
+           handshake-channel mutex
+           sender-waiting?
+           immediate-receive late-receive
+           )
+  )
+
+(define no-data '(no-data))
+(define receiver-waiting '(receiver-waiting))
+
+(define-class <channel> ())
+
+(define-class <data-channel> (<channel>)
+  (handshake-channel #:accessor handshake-channel)
+  (data #:accessor data #:init-value no-data)
+  (cv #:accessor cv #:init-form (make-condition-variable))
+  (mutex #:accessor mutex #:init-form (make-mutex)))
+
+(define-method (initialize (ch <data-channel>) initargs)
+  (next-method)
+  (set! (handshake-channel ch) ch))
+
+(define-method (make-channel)
+  (make <data-channel>))
+
+(define-method (sender-waiting? (ch <data-channel>))
+  (not (eq? (data ch) no-data)))
+
+(define-method (receiver-waiting? (ch <data-channel>))
+  (eq? (data ch) receiver-waiting))
+
+(define-method (immediate-receive (ch <data-channel>))
+  (signal-condition-variable (cv ch))
+  (let ((res (data ch)))
+    (set! (data ch) no-data)
+    res))
+
+(define-method (late-receive (ch <data-channel>))
+  (let ((res (data ch)))
+    (set! (data ch) no-data)
+    res))
+
+(define-method (? (ch <data-channel>))
+  (lock-mutex (mutex ch))
+  (let ((res (cond ((receiver-waiting? ch)
+                   (unlock-mutex (mutex ch))
+                   (scm-error 'misc-error '?
+                              "another process is already receiving on ~A"
+                              (list ch) #f))
+                  ((sender-waiting? ch)
+                   (immediate-receive ch))
+                  (else
+                   (set! (data ch) receiver-waiting)
+                   (wait-condition-variable (cv ch) (mutex ch))
+                   (late-receive ch)))))
+    (unlock-mutex (mutex ch))
+    res))
+
+(define-method (! (ch <data-channel>))
+  (! ch *unspecified*))
+
+(define-method (! (ch <data-channel>) (x <top>))
+  (lock-mutex (mutex (handshake-channel ch)))
+  (cond ((receiver-waiting? ch)
+        (set! (data ch) x)
+        (signal-condition-variable (cv (handshake-channel ch))))
+       ((sender-waiting? ch)
+        (unlock-mutex (mutex (handshake-channel ch)))
+        (scm-error 'misc-error '! "another process is already sending on ~A"
+                   (list ch) #f))
+       (else
+        (set! (data ch) x)
+        (wait-condition-variable (cv ch) (mutex ch))))
+  (unlock-mutex (mutex (handshake-channel ch))))
+
+;;; Add protocols?
+
+(define-class <port-channel> (<channel>)
+  (port #:accessor port #:init-keyword #:port))
+
+(define-method (make-channel (port <port>))
+  (make <port-channel> #:port port))
+
+(define-method (? (ch <port-channel>))
+  (read (port ch)))
+
+(define-method (! (ch <port-channel>))
+  (write (port ch)))
+
+(define-class <timer-channel> (<channel>))
+
+(define the-timer (make <timer-channel>))
+
+(define timer-cv (make-condition-variable))
+(define timer-mutex (make-mutex))
+
+(define (make-timer)
+  the-timer)
+
+(define (timeofday->us t)
+  (+ (* 1000000 (car t)) (cdr t)))
+
+(define (us->timeofday n)
+  (cons (quotient n 1000000)
+       (remainder n 1000000)))
+
+(define-method (? (ch <timer-channel>))
+  (timeofday->us (gettimeofday)))
+
+(define-method (? (ch <timer-channel>) (t <integer>))
+  (lock-mutex timer-mutex)
+  (wait-condition-variable timer-cv timer-mutex (us->timeofday t))
+  (unlock-mutex timer-mutex))
+
+;;; (alt CLAUSE ...)
+;;;
+;;; CLAUSE ::= ((? CH) FORM ...)
+;;;            | (EXP (? CH) FORM ...)
+;;;            | (EXP FORM ...)
+;;;
+;;; where FORM ... can be => (lambda (x) ...)
+;;;
+;;; *fixme* Currently only handles <data-channel>:s
+;;;
+
+(define-syntax oc:lock
+  (syntax-rules (?)
+    ((_ ((? ch) form ...)) (lock-mutex (mutex ch)))
+    ((_ (exp (? ch) form ...)) (lock-mutex (mutex ch)))
+    ((_ (exp form ...)) #f)))
+
+(define-syntax oc:unlock
+  (syntax-rules (?)
+    ((_ ((? ch) form ...)) (unlock-mutex (mutex ch)))
+    ((_ (exp (? ch) form ...)) (unlock-mutex (mutex ch)))
+    ((_ (exp form ...)) #f)))
+
+(define-syntax oc:consequence
+  (syntax-rules (=>)
+    ((_ data) data)
+    ((_ data => (lambda (x) e1 e2 ...))
+     (let ((x data)) e1 e2 ...))
+    ((_ data e1 e2 ...)
+     (begin data e1 e2 ...))))
+
+(define-syntax oc:immediate-dispatch
+  (syntax-rules (?)
+    ((_ ((? ch) e1 ...))
+     ((sender-waiting? ch)
+      (oc:consequence (immediate-receive ch) e1 ...)))
+    ((_ (exp (? ch) e1 ...))
+     ((and exp (sender-waiting? ch))
+      (oc:consequence (immediate-receive ch) e1 ...)))
+    ((_ (exp e1 ...))
+     (exp e1 ...))))
+
+(define-syntax oc:late-dispatch
+  (syntax-rules (?)
+    ((_ ((? ch) e1 ...))
+     ((sender-waiting? ch)
+      (oc:consequence (late-receive ch) e1 ...)))
+    ((_ (exp (? ch) e1 ...))
+     ((and exp (sender-waiting? ch))
+      (oc:consequence (late-receive ch) e1 ...)))
+    ((_ (exp e1 ...))
+     (#f))))
+
+(define-syntax oc:first-channel
+  (syntax-rules (?)
+    ((_ ((? ch) e1 ...) c2 ...)
+     ch)
+    ((_ (exp (? ch) e1 ...) c2 ...)
+     ch)
+    ((_ c1 c2 ...)
+     (first-channel c2 ...))))
+
+(define-syntax oc:set-handshake-channel
+  (syntax-rules (?)
+    ((_ ((? ch) e1 ...) handshake)
+     (set! (handshake-channel ch) handshake))
+    ((_ (exp (? ch) e1 ...) handshake)
+     (and exp (set! (handshake-channel ch) handshake)))
+    ((_ (exp e1 ...) handshake)
+     #f)))
+
+(define-syntax oc:unset-handshake-channel
+  (syntax-rules (?)
+    ((_ ((? ch) e1 ...))
+     (set! (handshake-channel ch) ch))
+    ((_ (exp (? ch) e1 ...))
+     (and exp (set! (handshake-channel ch) ch)))
+    ((_ (exp e1 ...))
+     #f)))
+
+(define-syntax alt
+  (lambda (x)
+    (define (else-clause? x)
+      (syntax-case x (else)
+       ((_) #f)
+       ((_ (else e1 e2 ...)) #t)
+       ((_ c1 c2 ...) (else-clause? (syntax (_ c2 ...))))))
+    
+    (syntax-case x (else)
+      ((_ c1 c2 ...)
+       (else-clause? x)
+       (syntax (begin
+                (oc:lock c1)
+                (oc:lock c2) ...
+                (let ((res (cond (oc:immediate-dispatch c1)
+                                 (oc:immediate-dispatch c2) ...)))
+                  (oc:unlock c1)
+                  (oc:unlock c2) ...
+                  res))))
+      ((_ c1 c2 ...)
+       (syntax (begin
+                (oc:lock c1)
+                (oc:lock c2) ...
+                (let ((res (cond (oc:immediate-dispatch c1)
+                                 (oc:immediate-dispatch c2) ...
+                                 (else (let ((ch (oc:first-channel c1 c2 ...)))
+                                         (oc:set-handshake-channel c1 ch)
+                                         (oc:set-handshake-channel c2 ch) ...
+                                         (wait-condition-variable (cv ch)
+                                                                  (mutex ch))
+                                         (oc:unset-handshake-channel c1)
+                                         (oc:unset-handshake-channel c2) ...
+                                         (cond (oc:late-dispatch c1)
+                                               (oc:late-dispatch c2) ...))))))
+                  (oc:unlock c1)
+                  (oc:unlock c2) ...
+                  res)))))))