(define-struct allocation
(process
units))
(define-values (struct:resource
resource-constructor
resource?
resource-field-ref
set-resource-field!)
(make-struct-type 'resource #f 5 0))
(define resource-units
(make-struct-field-accessor resource-field-ref 0 'units))
(define resource-units-available
(make-struct-field-accessor resource-field-ref 1 'units-available))
(define set-resource-units-available!
(make-struct-field-mutator set-resource-field! 1 'units-available))
(define resource-units-allocated
(make-struct-field-accessor resource-field-ref 2 'units-allocated))
(define set-resource-units-allocated!
(make-struct-field-mutator set-resource-field! 2 'units-allocated))
(define resource-satisfied
(make-struct-field-accessor resource-field-ref 3 'satisfied))
(define set-resource-satisfied!
(make-struct-field-mutator set-resource-field! 3 'satisfied))
(define resource-queue
(make-struct-field-accessor resource-field-ref 4 'queue))
(define set-resource-queue!
(make-struct-field-mutator set-resource-field! 4 'queue))
(define make-resource
(case-lambda
((units)
(resource-constructor
units units 0 (make-set) (make-set))) (()
(make-resource 1))))
(define (resource-process-allocation resource process)
(let/ec exit
(set-for-each (resource-satisfied resource)
(lambda (allocation)
(if (eq? (allocation-process allocation) process)
(exit allocation))))
#f))
(define (resource-allocate resource units process)
(let ((process-allocation (resource-process-allocation resource process)))
(if process-allocation
(set-allocation-units!
process-allocation
(+ (allocation-units process-allocation) units))
(set-insert-last!
(resource-satisfied resource)
(make-allocation process units)))
(set-resource-units-available!
resource
(- (resource-units-available resource) units))
(set-resource-units-allocated!
resource
(+ (resource-units-allocated resource) units))))
(define (resource-deallocate resource units process)
(let ((process-allocation (resource-process-allocation resource process)))
(if (< units (allocation-units process-allocation))
(set-allocation-units!
process-allocation
(- (allocation-units process-allocation) units))
(set-remove! (resource-satisfied resource)
process-allocation))
(set-resource-units-available!
resource
(+ (resource-units-available resource) units))
(set-resource-units-allocated!
resource
(- (resource-units-allocated resource) units))))
(define resource-request
(case-lambda
((resource units)
(let ((process (current-simulation-process)))
(if (not process)
(error 'resource-request
"no current process"))
(let ((process-allocation (resource-process-allocation resource process)))
(if (and process-allocation
(> (+ (allocation-units process-allocation) units)
(resource-units resource)))
(error 'resource-request
"units requested for process exceeds units available")))
(let/cc continuation
(if (> units (resource-units-available resource))
(let ((new-allocation (make-allocation process units))
(event (process-event process)))
(set-event-function! event continuation)
(set-event-arguments! event '())
(set-insert-last!
(resource-queue resource) new-allocation)
((current-simulation-loop-next)))
(resource-allocate resource units process)))))
((resource)
(resource-request resource 1))))
(define resource-relinquish
(case-lambda
((resource units)
(let ((process (current-simulation-process)))
(if (not process)
(error 'resource-relinquish
"no current process"))
(let ((process-allocation (resource-process-allocation resource process)))
(if (not process-allocation)
(error 'resource-relinquish
"attempt to release units when none are allocated"))
(if (> units (allocation-units process-allocation))
(error 'resource-relinquish
"attempt to relase more units than allocated"))
(resource-deallocate resource units process)
(let/ec exit
(set-for-each-cell (resource-queue resource)
(lambda (cell)
(if (= (resource-units-available resource) 0)
(exit))
(let ((allocation (set-cell-item cell)))
(if (<= (allocation-units allocation)
(resource-units-available resource))
(let ((process (allocation-process allocation)))
(set-remove-cell!
(resource-queue resource)
cell)
(resource-allocate
resource (allocation-units allocation) process)
(schedule-event (process-event process) 'now))))))))
(void)))
((resource)
(let ((process (current-simulation-process)))
(if (not process)
(error 'resource-request
"no current process"))
(let ((process-allocation (resource-process-allocation resource process)))
(if (not process-allocation)
(error 'resource-request
"attempt to release units when none are allocated"))
(resource-relinquish resource (allocation-units process-allocation)))))))
(define (resource-queue-variable-n resource)
(set-variable-n (resource-queue resource)))
(define (resource-satisfied-variable-n resource)
(set-variable-n (resource-satisfied resource)))
(define-syntax with-resource
(syntax-rules ()
((with-resource (resource units)
body ...)
(begin
(resource-request resource units)
body ...
(resource-relinquish resource units)))
((with-resource (resource)
body ...)
(with-resource (resource 1)
body ...))))