#region License Information
/* SimSharp - A .NET port of SimPy, discrete event simulation framework
Copyright (C) 2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
namespace SimSharp {
public class PreemptiveResource {
public int Capacity { get; protected set; }
public int InUse { get { return Users.Count; } }
public int Remaining { get { return Capacity - InUse; } }
protected Environment Environment { get; private set; }
protected SortedList> RequestQueue { get; private set; }
protected Queue ReleaseQueue { get; private set; }
protected HashSet Users { get; private set; }
public PreemptiveResource(Environment environment, int capacity = 1) {
if (capacity <= 0) throw new ArgumentException("Capacity must be > 0.", "capacity");
Environment = environment;
Capacity = capacity;
RequestQueue = new SortedList>();
ReleaseQueue = new Queue();
Users = new HashSet();
}
public virtual PreemptiveRequest Request(int priority = 1, bool preempt = false) {
var request = new PreemptiveRequest(Environment, TriggerRelease, DisposeCallback, priority, preempt);
if (!RequestQueue.ContainsKey(request.Priority))
RequestQueue.Add(request.Priority, new LinkedList());
RequestQueue[request.Priority].AddLast(request);
TriggerRequest();
return request;
}
public virtual Release Release(PreemptiveRequest request) {
var release = new Release(Environment, request, TriggerRequest);
ReleaseQueue.Enqueue(release);
TriggerRelease();
return release;
}
protected void DisposeCallback(Event @event) {
var request = @event as PreemptiveRequest;
if (request != null) Release(request);
}
protected virtual void DoRequest(PreemptiveRequest request) {
if (Users.Count >= Capacity && request.Preempt) {
// Check if we can preempt another process
var oldest = Users.OfType().Select((r, i) => new { Request = r, Index = i })
.OrderByDescending(x => x.Request.Priority)
.ThenByDescending(x => x.Request.Time)
.ThenByDescending(x => x.Request.Preempt)
.ThenByDescending(x => x.Index)
.First().Request;
if (oldest.Priority > request.Priority || (oldest.Priority == request.Priority
&& (!oldest.Preempt && request.Preempt || (oldest.Preempt == request.Preempt
&& oldest.Time > request.Time)))) {
Users.Remove(oldest);
oldest.Process.Interrupt(new Preempted(request.Process, oldest.Time));
}
}
if (Users.Count < Capacity) {
Users.Add(request);
request.Succeed();
}
}
protected virtual void DoRelease(Release release) {
if (!Users.Remove(release.Request)) {
var preemptRequest = release.Request as PreemptiveRequest;
if (preemptRequest != null) {
var current = RequestQueue[preemptRequest.Priority].First;
while (current != null && current.Value != release.Request)
current = current.Next;
if (current != null) RequestQueue[preemptRequest.Priority].Remove(current);
}
}
release.Succeed();
}
protected virtual void TriggerRequest(Event @event = null) {
foreach (var entry in RequestQueue) {
var requests = entry.Value;
var current = requests.First;
while (current != null) {
var request = current.Value;
DoRequest(request);
if (request.IsTriggered) {
var next = current.Next;
requests.Remove(current);
current = next;
} else current = current.Next;
}
}
}
protected virtual void TriggerRelease(Event @event = null) {
while (ReleaseQueue.Count > 0) {
var release = ReleaseQueue.Peek();
DoRelease(release);
if (release.IsTriggered) {
ReleaseQueue.Dequeue();
} else break;
}
}
}
}