#region License Information
/* SimSharp - A .NET port of SimPy, discrete event simulation framework
Copyright (C) 2019 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;
namespace SimSharp {
///
/// A resource holds a fixed number of anonymous entities.
///
/// Requests are processed in FIFO order.
/// Releases are processed in FIFO order (usually no simulation time passes for a Release).
///
public class Resource {
public int Capacity { get; protected set; }
public int InUse { get { return Users.Count; } }
public int Remaining { get { return Capacity - InUse; } }
protected Simulation Environment { get; private set; }
protected LinkedList RequestQueue { get; private set; }
protected Queue ReleaseQueue { get; private set; }
protected HashSet Users { get; private set; }
protected List WhenAnyQueue { get; private set; }
protected List WhenFullQueue { get; private set; }
protected List WhenEmptyQueue { get; private set; }
protected List WhenChangeQueue { get; private set; }
public Resource(Simulation environment, int capacity = 1) {
if (capacity <= 0) throw new ArgumentException("Capacity must > 0.", "capacity");
Environment = environment;
Capacity = capacity;
RequestQueue = new LinkedList();
ReleaseQueue = new Queue();
Users = new HashSet();
WhenAnyQueue = new List();
WhenFullQueue = new List();
WhenEmptyQueue = new List();
WhenChangeQueue = new List();
}
public virtual Request Request() {
var request = new Request(Environment, TriggerRelease, DisposeCallback);
RequestQueue.AddLast(request);
TriggerRequest();
return request;
}
public virtual Release Release(Request request) {
var release = new Release(Environment, request, TriggerRequest);
ReleaseQueue.Enqueue(release);
TriggerRelease();
return release;
}
public virtual Event WhenAny() {
var whenAny = new Event(Environment);
WhenAnyQueue.Add(whenAny);
TriggerWhenAny();
return whenAny;
}
public virtual Event WhenFull() {
var whenFull = new Event(Environment);
WhenFullQueue.Add(whenFull);
TriggerWhenFull();
return whenFull;
}
public virtual Event WhenEmpty() {
var whenEmpty = new Event(Environment);
WhenEmptyQueue.Add(whenEmpty);
TriggerWhenEmpty();
return whenEmpty;
}
public virtual Event WhenChange() {
var whenChange = new Event(Environment);
WhenChangeQueue.Add(whenChange);
return whenChange;
}
protected virtual void DisposeCallback(Event @event) {
var request = @event as Request;
if (request != null) {
Release(request);
}
}
protected virtual void DoRequest(Request request) {
if (Users.Count < Capacity) {
Users.Add(request);
request.Succeed();
}
}
protected virtual void DoRelease(Release release) {
if (!Users.Remove(release.Request))
throw new InvalidOperationException("Released request does not have a user.");
release.Succeed();
}
protected virtual void TriggerRequest(Event @event = null) {
while (RequestQueue.Count > 0) {
var request = RequestQueue.First.Value;
DoRequest(request);
if (request.IsTriggered) {
RequestQueue.RemoveFirst();
TriggerWhenEmpty();
TriggerWhenChange();
} else break;
}
}
protected virtual void TriggerRelease(Event @event = null) {
while (ReleaseQueue.Count > 0) {
var release = ReleaseQueue.Peek();
if (release.Request.IsAlive) {
if (!RequestQueue.Remove(release.Request))
throw new InvalidOperationException("Failed to cancel a request.");
release.Succeed();
ReleaseQueue.Dequeue();
} else {
DoRelease(release);
if (release.IsTriggered) {
ReleaseQueue.Dequeue();
TriggerWhenAny();
TriggerWhenFull();
TriggerWhenChange();
} else break;
}
}
}
protected virtual void TriggerWhenAny() {
if (Remaining > 0) {
if (WhenAnyQueue.Count == 0) return;
foreach (var evt in WhenAnyQueue)
evt.Succeed();
WhenAnyQueue.Clear();
}
}
protected virtual void TriggerWhenFull() {
if (InUse == 0) {
if (WhenFullQueue.Count == 0) return;
foreach (var evt in WhenFullQueue)
evt.Succeed();
WhenFullQueue.Clear();
}
}
protected virtual void TriggerWhenEmpty() {
if (Remaining == 0) {
if (WhenEmptyQueue.Count == 0) return;
foreach (var evt in WhenEmptyQueue)
evt.Succeed();
WhenEmptyQueue.Clear();
}
}
protected virtual void TriggerWhenChange() {
if (WhenChangeQueue.Count == 0) return;
foreach (var evt in WhenChangeQueue)
evt.Succeed();
WhenChangeQueue.Clear();
}
}
}