3.47 Exercise 3.47
An implementation of semaphores in terms of mutexes can proceed something like this:
A semaphore allows n concurrently-executing procedures at once. We can imagine checking this as equivalent to checking if any of n mutexes are unset.
(define (make-semaphore n) (define (get-available-mutex ms) (cond ((null? ms) null) ((car ms) ((car ms) 'acquire) (else (get-available-mutex ms))))) (define (make-mutexes times) (if (= 0 times) null (cons (make-mutex) (make-mutexes (- times 1))))) (let ((mutexes (make-mutexes n))) (define (the-semaphore m) (cond ((eq? m 'acquire) (let ((acquired (get-available-mutex mutexes))) (if (null? acquired) (the-semaphore 'acquire) (lambda () (acquired 'release))))) (else (error "UNKNOWN MESSAGE -- " m)))) the-semaphore))
As our convention, is a slot in the semaphore is acquired, it returns a no-argument procedure that will release itself when called, so that the owner can relinquish control.
We could also implement the same idea using test-and-set! calls. Note the essential similarity between the two:
(define (make-semaphore n) (define (get-available-cell cells) (if (null? cells) null (let ((result (test-and-set! (car cells)))) (if result (get-available-cell (cdr cells)) (car cells))))) (define (make-cells times) (if (= 0 times) null (cons (list false) (make-cells (- times 1))))) (let ((cells (make-cells n))) (define (the-semaphore m) (cond ((eq? m 'acquire) (let ((acquired (get-available-cell cells))) (if (null? acquired) (the-semaphore 'acquire) (lambda () (set-car! acquired false))))) (else (error "UNKNOWN MESSAGE -- " m)))) the-semaphore))