#lang racket
;;; Example 6e - Selective Calls with Reneg - Version 1

(require (planet williams/simulation/simulation))

;;; process (lock)
;;; entry (lock) -> void?
;;; entry (unlock) -> void?
(define-process (lock)
  (let ((process #f)
        (count 0))
    (let loop ()
        ((accept caller (lock)
           (if process
               (if (eq? caller process)
                   (set! count (+ count 1))
                 (set! process caller)
                 (set! count 1)))))
        ((accept caller (unlock)
           (if (eq? caller process)
                 (set! count (- count 1))
                 (when (= count 0)
                   (set! process #f)))
               (error 'unlock
                      "process does not have the lock"

;;; process (p1 i a-lock)
;;;   i : exact-nonnegative-integer?
;;;   a-lock : process?
;;; This process uses the lock to protect its critical region, which just does a
;;; random wait in this case. A process instance will reneg on the lock call if
;;; it is not accepted within 10.0 time units.
(define-process (p1 i a-lock)
  (let/ec exit
    (printf "~a: process p1(~a) started.~n"
            (current-simulation-time) i)
      ((call a-lock (lock)))
      (else #:in 10.0
        (printf "~a: process p1(~a) gave up.~n"
                (current-simulation-time) i)
    (printf "~a: process p1(~a) acquired lock.~n"
            (current-simulation-time) i)
    (wait (random-flat 0.0 10.0))
    (printf "~a: process p1(~a) releasing lock.~n"
            (current-simulation-time) i)
    (call a-lock (unlock))))

;;; (main n) -> void?
;;;   n : exact-nonnegative-integer?
;;; Creates the lock process and schedules n p1 processes that use the lock.
(define (main n)
    (let ((a-lock (schedule #:now (lock))))
      (for ((i (in-range n)))
        (schedule #:at (random-flat 0.0 10.0) (p1 i a-lock)))

;;; Test with 10 p1 processes.
(main 10)