#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(); } } }