;;; sellers.scm ;; 1. Defs of serializer primitives. ;; 2. Defs for ticket selling exercise. ;; 3. Procedure to be modified: make-ticket-seller ;; 4. (test) illustrates the problem with the unserialized make-ticket-seller. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; 1. Definition of our parallel-execute and make-serializer primitives (define (parallel-execute . args) (let ((threads (map thread args))) (for-each thread-wait threads))) ;; The serializing procedure (define (make-serializer) (let ((mutex (make-mutex))) (lambda (p) (define (serialized-p . args) (mutex 'acquire) (let ((value (apply p args))) (mutex 'release) value)) serialized-p))) (define (make-mutex) (let ((cell (make-semaphore 1))) (define (the-mutex m) (cond ((eq? m 'acquire) (semaphore-wait cell)) ((eq? m 'release) (semaphore-post cell)))) the-mutex)) ;;; Do not modify any of the above code. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; 2. ;; Also do not modify this section. (define (customer-order seats) (if (> seats 0); customer can buy (begin (sleep (random 2)) ;customer thinks for a while #t) ;and then says "I'll take one ticket, please" #f) ; seller says "sorry, sold out." ) ;; To speed up testing, use this version of sleep rather than built-in one. (define sleep-factor 2000) ; make this number larger or smaller as needed ; so that (test) doesn't take too long, but still ; shows concurrency problems before you ; modify make-ticket-seller (define deep-sleep sleep) (define (sleep k) (define (sleep-iter x) (if (> x 0) (sleep-iter (- x 1)))) (sleep-iter (* k sleep-factor)) ) (define *SEATS-LEFT* 0) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 3. Create a ticket seller which, when started, continuously processes ;; customer orders until there are no seats left. ;; Accesses and modifies global variable *SEATS-LEFT*. (define (make-ticket-seller name) (let ((total-tickets-sold 0)) (define (sell) (sleep (random 2)) ;sleep up to two seconds until customer arrives (if (customer-order *SEATS-LEFT*) (begin (set! *SEATS-LEFT* (- *SEATS-LEFT* 1)) ;record the sale ;globally (set! total-tickets-sold (+ total-tickets-sold 1)) ; local ; record (sell)) ;continue selling (print-tickets-sold name total-tickets-sold) )) sell)) ;; Prints out how many ticks the ticket seller sold. (define (print-tickets-sold name n) (display name) (display " sold ") (sleep (+ 3 (random 2))) ; show effect of delays during printing (display n) (sleep (+ 3 (random 2))) ; show effect of delays during printing (display " tickets") (sleep (+ 1 (random 2))) ; show effect of delays during printing (newline)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 4. ;; Create 100 threads of ticket sellers (define (test) (set! *SEATS-LEFT* 100) (parallel-execute (make-ticket-seller "A") (make-ticket-seller "B") (make-ticket-seller "C") (make-ticket-seller "D") (make-ticket-seller "E") (make-ticket-seller "F") (make-ticket-seller "G") (make-ticket-seller "H") (make-ticket-seller "I") (make-ticket-seller "J") ) (display "Total seats sold ") (display (- 100 *SEATS-LEFT*)) (newline) ) (display "To run the test: (test) ...try it several times. Note too many tickets usually are sold.") (newline)