compatibility/collects/mzlib/thread.ss
Matthew Flatt 20a62e5305 .
original commit: 129e2b2fddb36c4a408af6327233ca9fd3fa0d36
2004-05-10 22:54:09 +00:00

138 lines
3.7 KiB
Scheme

(module thread mzscheme
(require "spidey.ss"
"etc.ss")
(provide consumer-thread
with-semaphore
dynamic-disable-break
dynamic-enable-break
make-single-threader
run-server)
#|
t accepts a function, f, and creates a thread. It returns the thread and a
function, g. When g is applied it passes it's argument to f, and evaluates
the call of f in the time of the thread that was created. Calls to g do not
block.
|#
(define consumer-thread
(case-lambda
[(f) (consumer-thread f void)]
[(f init)
(unless (procedure? f) (raise-type-error 'consumer-thread "procedure" f))
(let ([sema (make-semaphore 0)]
[protect (make-semaphore 1)]
[front-state null]
[back-state null])
(values
(thread
(letrec ([loop
(lambda ()
(semaphore-wait sema)
(let ([local-state
(begin
(semaphore-wait protect)
(if (null? back-state)
(let ([new-front (reverse front-state)])
(set! back-state (cdr new-front))
(set! front-state null)
(semaphore-post protect)
(car new-front))
(begin0
(car back-state)
(set! back-state (cdr back-state))
(semaphore-post protect))))])
(apply f local-state))
(loop))])
(lambda ()
(init)
(loop))))
(lambda new-state
(let ([num (length new-state)])
(unless (procedure-arity-includes? f num)
(raise
(make-exn:fail:contract:arity
(format "<procedure-from-consumer-thread>: consumer procedure arity is ~e; provided ~s argument~a"
(procedure-arity f) num (if (= 1 num) "" "s"))
(current-continuation-marks)))))
(semaphore-wait protect)
(set! front-state (cons new-state front-state))
(semaphore-post protect)
(semaphore-post sema))))]))
(define with-semaphore
(lambda (s f)
(semaphore-wait s)
(begin0 (f)
(semaphore-post s))))
(define dynamic-enable-break
(polymorphic
(lambda (thunk)
(parameterize-break #t
(thunk)))))
(define dynamic-disable-break
(polymorphic
(lambda (thunk)
(parameterize-break #f
(thunk)))))
(define make-single-threader
(polymorphic
(lambda ()
(let ([sema (make-semaphore 1)])
(lambda (thunk)
(dynamic-wind
(lambda () (semaphore-wait sema))
thunk
(lambda () (semaphore-post sema))))))))
(define run-server
(opt-lambda (port-number
handler
connection-timeout
[handle-exn void]
[tcp-listen tcp-listen]
[tcp-close tcp-close]
[tcp-accept tcp-accept]
[tcp-accept/enable-break tcp-accept/enable-break])
(let ([l (tcp-listen port-number)]
[can-break? (break-enabled)])
(dynamic-wind
void
(lambda ()
;; loop to handle connections
(let loop ()
(with-handlers ([exn:fail:network? handle-exn])
;; Make a custodian for the next session:
(let ([c (make-custodian)])
(parameterize ([current-custodian c])
;; disable breaks during session set-up...
(parameterize-break #f
;; ... but enable breaks while blocked on an accept:
(let-values ([(r w) ((if can-break?
tcp-accept/enable-break
tcp-accept)
l)])
;; Handler thread:
(let ([t (thread (lambda ()
(when can-break?
(break-enabled #t))
(handler r w)))])
;; Clean-up and timeout thread:
(thread (lambda ()
(sync/timeout connection-timeout t)
(when (thread-running? t)
;; Only happens if connection-timeout is not #f
(break-thread t))
(sync/timeout connection-timeout t)
(custodian-shutdown-all c)))))))))
(loop)))
(lambda () (tcp-close l)))))))