source: stable/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.1.1/SimSharp-3.1.1/Core/Resources/PreemptiveResource.cs @ 17053

Last change on this file since 17053 was 17053, checked in by abeham, 2 years ago

#2975: merged to stable

File size: 7.1 KB
Line 
1#region License Information
2/* SimSharp - A .NET port of SimPy, discrete event simulation framework
3Copyright (C) 2019  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
17#endregion
18
19using System;
20using System.Collections.Generic;
21using System.Linq;
22
23namespace SimSharp {
24  /// <summary>
25  /// A PreemptiveResource is similar to a <see cref="PriorityResource"/>. However,
26  /// it may be possible to interrupt a lower-priority user and hand over the resource.
27  ///
28  /// PreemptiveResource holds a fixed amount of anonymous entities.
29  /// Requests are processed in this order: priority, time, preemption, and finally FIFO.
30  /// Releases are processed in FIFO order (usually no simulation time passes for a Release).
31  /// </summary>
32  /// <remarks>
33  /// Working with PreemptiveResource, a process holding a request must always call
34  /// <see cref="Process.HandleFault"/> after yielding an event and handle a potential
35  /// interruption.
36  /// </remarks>
37  public class PreemptiveResource {
38
39    public int Capacity { get; protected set; }
40
41    public int InUse { get { return Users.Count; } }
42
43    public int Remaining { get { return Capacity - InUse; } }
44
45    protected Simulation Environment { get; private set; }
46
47    protected SimplePriorityQueue<PreemptiveRequest> RequestQueue { get; private set; }
48    protected Queue<Release> ReleaseQueue { get; private set; }
49    protected HashSet<PreemptiveRequest> Users { get; private set; }
50    protected List<Event> WhenAnyQueue { get; private set; }
51    protected List<Event> WhenFullQueue { get; private set; }
52    protected List<Event> WhenEmptyQueue { get; private set; }
53    protected List<Event> WhenChangeQueue { get; private set; }
54
55    public PreemptiveResource(Simulation environment, int capacity = 1) {
56      if (capacity <= 0) throw new ArgumentException("Capacity must be > 0.", "capacity");
57      Environment = environment;
58      Capacity = capacity;
59      RequestQueue = new SimplePriorityQueue<PreemptiveRequest>();
60      ReleaseQueue = new Queue<Release>();
61      Users = new HashSet<PreemptiveRequest>();
62      WhenAnyQueue = new List<Event>();
63      WhenFullQueue = new List<Event>();
64      WhenEmptyQueue = new List<Event>();
65      WhenChangeQueue = new List<Event>();
66    }
67
68    public virtual PreemptiveRequest Request(double priority = 1, bool preempt = false) {
69      var request = new PreemptiveRequest(Environment, TriggerRelease, DisposeCallback, priority, preempt);
70      RequestQueue.Enqueue(request);
71      TriggerRequest();
72      return request;
73    }
74
75    public virtual Release Release(PreemptiveRequest request) {
76      var release = new Release(Environment, request, TriggerRequest);
77      ReleaseQueue.Enqueue(release);
78      TriggerRelease();
79      return release;
80    }
81
82    public virtual Event WhenAny() {
83      var whenAny = new Event(Environment);
84      WhenAnyQueue.Add(whenAny);
85      TriggerWhenAny();
86      return whenAny;
87    }
88
89    public virtual Event WhenFull() {
90      var whenFull = new Event(Environment);
91      WhenFullQueue.Add(whenFull);
92      TriggerWhenFull();
93      return whenFull;
94    }
95
96    public virtual Event WhenEmpty() {
97      var whenEmpty = new Event(Environment);
98      WhenEmptyQueue.Add(whenEmpty);
99      TriggerWhenEmpty();
100      return whenEmpty;
101    }
102
103    public virtual Event WhenChange() {
104      var whenChange = new Event(Environment);
105      WhenChangeQueue.Add(whenChange);
106      return whenChange;
107    }
108
109    protected void DisposeCallback(Event @event) {
110      var request = @event as PreemptiveRequest;
111      if (request != null) Release(request);
112    }
113
114    protected virtual void DoRequest(PreemptiveRequest request) {
115      if (Users.Count >= Capacity && request.Preempt) {
116        // Check if we can preempt another process
117        // MaxItems are the least important according to priorty, time, and preemption flag
118        var preempt = Users.MaxItems(x => x).Last();
119        if (preempt.CompareTo(request) > 0) {
120          preempt.IsPreempted = true;
121          Users.Remove(preempt);
122          preempt.Owner?.Interrupt(new Preempted(request.Owner, preempt.Time));
123        }
124      }
125      if (Users.Count < Capacity) {
126        Users.Add(request);
127        request.Succeed();
128      }
129    }
130
131    protected virtual void DoRelease(Release release) {
132      var req = (PreemptiveRequest)release.Request;
133      if (!Users.Remove(req) && !req.IsPreempted)
134        throw new InvalidOperationException("Released request does not have a user.");
135      release.Succeed();
136    }
137
138    protected virtual void TriggerRequest(Event @event = null) {
139      while (RequestQueue.Count > 0) {
140        var request = RequestQueue.First;
141        DoRequest(request);
142        if (request.IsTriggered) {
143          RequestQueue.Dequeue();
144          TriggerWhenEmpty();
145          TriggerWhenChange();
146        } else break;
147      }
148    }
149
150    protected virtual void TriggerRelease(Event @event = null) {
151      while (ReleaseQueue.Count > 0) {
152        var release = ReleaseQueue.Peek();
153        if (release.Request.IsAlive) {
154          if (!RequestQueue.TryRemove((PreemptiveRequest)release.Request))
155            throw new InvalidOperationException("Failed to cancel a request.");
156          release.Succeed();
157          ReleaseQueue.Dequeue();
158        } else {
159          DoRelease(release);
160          if (release.IsTriggered) {
161            ReleaseQueue.Dequeue();
162            TriggerWhenAny();
163            TriggerWhenFull();
164            TriggerWhenChange();
165          } else break;
166        }
167      }
168    }
169
170    protected virtual void TriggerWhenAny() {
171      if (Remaining > 0) {
172        if (WhenAnyQueue.Count == 0) return;
173        foreach (var evt in WhenAnyQueue)
174          evt.Succeed();
175        WhenAnyQueue.Clear();
176      }
177    }
178
179    protected virtual void TriggerWhenFull() {
180      if (InUse == 0) {
181        if (WhenFullQueue.Count == 0) return;
182        foreach (var evt in WhenFullQueue)
183          evt.Succeed();
184        WhenFullQueue.Clear();
185      }
186    }
187
188    protected virtual void TriggerWhenEmpty() {
189      if (Remaining == 0) {
190        if (WhenEmptyQueue.Count == 0) return;
191        foreach (var evt in WhenEmptyQueue)
192          evt.Succeed();
193        WhenEmptyQueue.Clear();
194      }
195    }
196
197    protected virtual void TriggerWhenChange() {
198      if (WhenChangeQueue.Count == 0) return;
199      foreach (var evt in WhenChangeQueue)
200        evt.Succeed();
201      WhenChangeQueue.Clear();
202    }
203  }
204}
Note: See TracBrowser for help on using the repository browser.