(module sicp-concurrency mzscheme
(require (only (lib "list.ss") first rest sort cons?)
(lib "contract.ss"))
(provide (all-from-except mzscheme #%app #%datum set!))
(define (thunk? datum)
(and (procedure? datum)
(procedure-arity-includes? datum 0)))
(define-struct cell (val))
(provide/contract [make-serializer (-> ((-> any) . -> . any))]
[parallel-execute (() (listof thunk?) . ->* . ((listof any/c)))]
[make-cell (boolean? . -> . cell?)]
[clear! (cell? . -> . any)]
[test-and-set! (cell? . -> . boolean?)])
(provide (rename timed-datum #%datum)
(rename timed-apply #%app)
(rename timed-set! set!))
(define yield-handler (lambda () (void)))
(define clock 0)
(define (start-timer ticks new-handler)
(set! yield-handler new-handler)
(set! clock ticks))
(define (stop-timer)
(let ([time-left clock])
(set! clock 0)
time-left))
(define (decrement-timer)
(if (> clock 0)
(begin
(set! clock (- clock 1))
(if (= clock 0)
(yield-handler)))))
(define (random-shuffle a-list)
(map rest
(sort (map (lambda (x) (cons (random) x)) a-list)
(lambda (x y) (< (first x) (first y))))))
(define (parallel-execute . thunks)
(round-robin (map make-engine (random-shuffle thunks))))
(define (dole-out-gas)
(add1 (random 12)))
(define (round-robin engs)
(if (null? engs)
'()
((car engs) (dole-out-gas)
(lambda (ticks value)
(cons value (round-robin (cdr engs))))
(lambda (eng)
(round-robin
(append (cdr engs) (list eng)))))))
(define make-engine
(let ([do-complete #f]
[do-expire #f])
(define (timer-handler)
(start-timer (call/cc do-expire) timer-handler))
(define (new-engine resume)
(lambda (ticks complete expire)
((call/cc (lambda (escape)
(set! do-complete
(lambda (ticks value)
(escape (lambda () (complete ticks value)))))
(set! do-expire
(lambda (resume)
(escape (lambda () (expire (new-engine resume))))))
(resume ticks))))))
(lambda (proc)
(new-engine
(lambda (ticks)
(start-timer ticks timer-handler)
(let ((value (proc)))
(let ((ticks (stop-timer)))
(do-complete ticks value))))))))
(define-syntax timed-datum
(syntax-rules ()
[(_ . datum)
(begin
(decrement-timer)
(#%datum . datum))]))
(define-syntax timed-apply
(syntax-rules ()
((_ operator rand ...)
(begin
(decrement-timer)
(#%app (begin (decrement-timer) operator)
(begin (decrement-timer) rand) ...)))))
(define-syntax timed-set!
(syntax-rules ()
((_ id expr)
(begin
(decrement-timer)
(set! id (begin
(let ([val expr])
(decrement-timer)
val)))))))
(define (make-serializer)
(let ((mutex (timed-apply make-mutex)))
(lambda (p)
(define (serialized-p . args)
(timed-apply mutex 'acquire)
(let ((val (timed-apply apply p args)))
(timed-apply mutex 'release)
val))
serialized-p)))
(define (make-mutex)
(let ((cell (timed-apply make-cell #f)))
(define (the-mutex m)
(cond ((timed-apply eq? m 'acquire)
(if (timed-apply test-and-set! cell)
(timed-apply the-mutex 'acquire)))
((timed-apply eq? m 'release)
(timed-apply clear! cell))))
the-mutex))
(define (clear! cell)
(timed-apply set-cell-val! cell #f))
(define (test-and-set! cell)
(if (cell-val cell)
#t
(begin (set-cell-val! cell #t)
#f))))