#lang scheme/base
(require "simulation-environment.ss"
"simulation-control.ss"
"event.ss"
"process.ss"
"set.ss")
(provide (all-defined-out))
(define-struct allocation
(process
units)
#:mutable)
(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)
(when (eq? (allocation-process allocation) process)
(exit allocation))))
#f))
(define (resource-process-queue resource process)
(let/ec exit
(set-for-each (resource-queue resource)
(lambda (allocation)
(when (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 priority reneg)
(let ((process (current-simulation-process)))
(when (not process)
(error 'resource-request
"no current process"))
(let ((process-allocation (resource-process-allocation resource process)))
(when (and process-allocation
(> (+ (allocation-units process-allocation) units)
(resource-units resource)))
(error 'resource-request
"units requested for process exceeds units available")))
(if (> units (resource-units-available resource))
(begin
(let/cc continuation
(let ((new-allocation (make-allocation process units))
(event (process-event process)))
(set-event-function! event continuation)
(set-event-arguments! event '())
(when reneg
(schedule-event event reneg))
(if (= priority -inf.0)
(set-insert-last!
(resource-queue resource) new-allocation)
(set-insert-priority!
(resource-queue resource) new-allocation priority))
((current-simulation-loop-next))))
(let ((process-queue (resource-process-queue resource process)))
(if process-queue
(begin
(set-remove! (resource-queue resource) process-queue)
#f)
#t)))
(begin
(resource-allocate resource units process)
#t))))
((resource units priority)
(resource-request resource units priority #f))
((resource units)
(resource-request resource units -inf.0 #f))
((resource)
(resource-request resource 1 -inf.0 #f))))
(define resource-relinquish
(case-lambda
((resource units)
(let ((process (current-simulation-process)))
(when (not process)
(error 'resource-relinquish
"no current process"))
(let ((process-allocation (resource-process-allocation resource process)))
(when (not process-allocation)
(error 'resource-relinquish
"attempt to release units when none are allocated"))
(when (> units (allocation-units process-allocation))
(error 'resource-relinquish
"attempt to relase more units than allocated"))
(resource-deallocate resource units process)
(when (not (set-empty? (resource-queue resource)))
(let ((highest (set-cell-priority
(set-first-cell
(resource-queue resource)))))
(let/ec exit
(set-for-each-cell (resource-queue resource)
(lambda (cell)
(when (or (= (resource-units-available resource) 0)
(and (set-cell-priority cell)
(< (set-cell-priority cell) highest)))
(exit))
(let ((allocation (set-cell-item cell)))
(when (<= (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)
(event-list-remove! (process-event process))
(schedule-event (process-event process) '#:now))))))))))
(void)))
((resource)
(let ((process (current-simulation-process)))
(when (not process)
(error 'resource-request
"no current process"))
(let ((process-allocation (resource-process-allocation resource process)))
(when (not process-allocation)
(error 'resource-request
"attempt to release units when none are allocated"))
(resource-relinquish resource (allocation-units process-allocation)))))))
(define-syntax request
(syntax-rules (now at in when)
((request "process keywords" resource u priority reneg (#:units units))
(request "process keywords" resource units priority reneg ()))
((request "process keywords" resource u priority reneg (#:units units . rest))
(request "process keywords" resource units priority reneg (rest)))
((request "process keywords" resource units p reneg (#:priority priority))
(request "process keywords" resource units priority reneg ()))
((request "process keywords" resource units p reneg (#:priority priority . rest))
(request "process keywords" resource units priority reneg (rest)))
((request "process keywords" resource units priority reneg (#:leave now))
(request "process keywords" resource units priority #:now ()))
((request "process keywords" resource units priority reneg (#:leave now . rest))
(request "process keywords" resource units priority #:now (rest)))
((request "process keywords" resource units priority reneg (#:leave (at time)))
(request "process keywords" resource units priority time ()))
((request "process keywords" resource units priority reneg (#:leave (at time) . rest))
(request "process keywords" resource units priority time (rest)))
((request "process keywords" resource units priority reneg (#:leave (in delay)))
(request "process keywords" resource units priority (+ (current-simulation-time) delay) ()))
((request "process keywords" resource units priority reneg (#:leave (in delay) . rest))
(request "process keywords" resource units priority (+ (current-simulation-time) delay) (rest)))
((request "process keywords" resource units priority reneg (#:leave (when event)))
(request "process keywords" resource units priority event ()))
((request "process keywords" resource units priority reneg (#:leave (when event) . rest))
(request "process keywords" resource units priority event (rest)))
((request "process keywords" resource units priority reneg (#:leave time))
(request "process keywords" resource units priority time ()))
((request "process keywords" resource units priority reneg (#:leave time . rest))
(request "process keywords" resource units priority time (rest)))
((request "process keywords" resource units priority reneg ())
(resource-request resource units priority reneg))
((request "process keywords" . rest)
(syntax-error rest))
((request resource)
(request "process keywords" resource 1 -inf.0 #f ()))
((request resource . rest)
(request "process keywords" resource 1 -inf.0 #f rest))
))
(define-syntax relinquish
(syntax-rules ()
((relinquish resource (#:units units))
(resource-relinquish resource units))
((relinquish resource)
(resource-relinquish resource))))
(define-syntax with-resource
(syntax-rules ()
((with-resource (resource . rest)
body ...)
(begin
(request resource . rest)
body ...
(relinquish resource)))))
(define (resource-queue-variable-n resource)
(set-variable-n (resource-queue resource)))
(define (resource-satisfied-variable-n resource)
(set-variable-n (resource-satisfied resource)))