1classdef Queue < ServiceStation
2 % A service station with queueing
4 % Copyright (c) 2012-2026, Imperial College London
18 function self = Queue(model, name, schedStrategy)
19 % SELF = QUEUE(MODEL, NAME, SCHEDSTRATEGY)
21 self@ServiceStation(name);
23 if model.isMatlabNative()
26 self.output = Dispatcher(
classes);
27 self.schedPolicy = SchedStrategyType.PR;
28 self.schedStrategy = SchedStrategy.PS;
29 self.serviceProcess = {};
31 self.numberOfServers = 1;
32 self.schedStrategyPar = zeros(1,length(model.getClasses()));
34 self.model.addNode(self);
38 self.delayoffTime = {};
39 self.pollingType = {};
40 self.switchoverTime = {};
41 self.patienceDistributions = {};
42 self.patienceTypes = {};
44 if nargin>=3 %exist(
'schedStrategy',
'var')
45 self.schedStrategy = schedStrategy;
46 switch SchedStrategy.toId(self.schedStrategy)
47 case {SchedStrategy.PS, SchedStrategy.DPS,SchedStrategy.GPS, SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO,SchedStrategy.GPSPRIO}
48 self.schedPolicy = SchedStrategyType.PR;
49 self.server = SharedServer(
classes);
50 case {SchedStrategy.LCFSPR, SchedStrategy.LCFSPRPRIO, SchedStrategy.LCFSPI, SchedStrategy.LCFSPIPRIO, SchedStrategy.EDF}
51 self.schedPolicy = SchedStrategyType.PR;
52 self.server = PreemptiveServer(
classes);
53 case {SchedStrategy.FCFS, SchedStrategy.LCFS, SchedStrategy.SIRO, SchedStrategy.SEPT, SchedStrategy.LEPT, SchedStrategy.SJF, SchedStrategy.LJF, SchedStrategy.EDD, SchedStrategy.SRPT, SchedStrategy.SRPTPRIO}
54 self.schedPolicy = SchedStrategyType.NP;
56 case SchedStrategy.INF
57 self.schedPolicy = SchedStrategyType.NP;
58 self.server = InfiniteServer(
classes);
59 self.numberOfServers = Inf;
60 case {SchedStrategy.HOL, SchedStrategy.FCFSPRIO, SchedStrategy.LCFSPRIO}
61 self.schedPolicy = SchedStrategyType.NP;
63 case SchedStrategy.POLLING
64 self.schedPolicy = SchedStrategyType.NP;
65 self.server = PollingServer(
classes);
67 line_error(mfilename,sprintf(
'The specified scheduling strategy (%s) is unsupported.',schedStrategy));
70 elseif model.isJavaNative()
72 switch SchedStrategy.toId(schedStrategy)
73 case SchedStrategy.INF
74 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.INF);
75 case SchedStrategy.FCFS
76 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FCFS);
77 case SchedStrategy.LCFS
78 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LCFS);
79 case SchedStrategy.SIRO
80 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SIRO);
81 case SchedStrategy.SJF
82 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SJF);
83 case SchedStrategy.LJF
84 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LJF);
86 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.PS);
87 case SchedStrategy.PSPRIO
88 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.PSPRIO);
89 case SchedStrategy.DPS
90 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.DPS);
91 case SchedStrategy.DPSPRIO
92 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.DPSPRIO);
93 case SchedStrategy.GPS
94 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.GPS);
95 case SchedStrategy.GPSPRIO
96 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.GPSPRIO);
97 case SchedStrategy.SEPT
98 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SEPT);
99 case SchedStrategy.LEPT
100 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LEPT);
101 case SchedStrategy.SRPT
102 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SRPT);
103 case SchedStrategy.SRPTPRIO
104 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SRPTPRIO);
105 case SchedStrategy.HOL
106 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.HOL);
107 case SchedStrategy.FORK
108 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FORK);
109 case SchedStrategy.EXT
110 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EXT);
111 case SchedStrategy.REF
112 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.REF);
113 case SchedStrategy.LCFSPR
114 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LCFSPR);
115 case SchedStrategy.EDD
116 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EDD);
117 case SchedStrategy.EDF
118 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EDF);
120 self.obj.setNumberOfServers(1);
121 self.index = model.obj.getNodeIndex(self.obj);
125 function setLoadDependence(self, alpha)
126 switch SchedStrategy.toId(self.schedStrategy)
127 case {SchedStrategy.PS, SchedStrategy.FCFS}
128 setLimitedLoadDependence(self, alpha);
130 line_error(mfilename,
'Load-dependence supported only for processor sharing (PS) and first-come first-serve (FCFS) stations.');
134 function setClassDependence(self, beta)
135 switch SchedStrategy.toId(self.schedStrategy)
136 case {SchedStrategy.PS, SchedStrategy.FCFS}
137 setLimitedClassDependence(self, beta);
139 line_error(mfilename,
'Class-dependence supported only for processor sharing (PS) and first-come first-serve (FCFS) stations.');
143 function setNumberOfServers(self, value)
144 % SETNUMBEROFSERVERS(VALUE)
146 switch SchedStrategy.toId(self.schedStrategy)
147 case SchedStrategy.INF
148 %line_warning(mfilename,'A request to change the number of servers in an infinite server node has been ignored.');
151 self.setNumServers(value);
154 self.obj.setNumberOfServers(value);
158 function setNumServers(self, value)
159 % SETNUMSERVERS(VALUE)
161 switch SchedStrategy.toId(self.schedStrategy)
162 case {SchedStrategy.DPS, SchedStrategy.GPS}
164 line_error(mfilename,sprintf(
'Cannot use multi-server stations with %s scheduling.', self.schedStrategy));
167 self.numberOfServers = value;
170 self.obj.setNumberOfServers(value);
174 function self = setStrategyParam(self,
class, weight)
175 % SELF = SETSTRATEGYPARAM(CLASS, WEIGHT)
177 self.schedStrategyPar(
class.index) = weight;
180 function distribution = getService(self,
class)
181 % DISTRIBUTION = GETSERVICE(CLASS)
183 %
return the service distribution assigned to the given
class
184 if nargin<2 %~exist(
'class',
'var')
185 for s = 1:length(self.model.getClasses())
186 classes = self.model.getClasses();
187 distribution{s} = self.server.serviceProcess{1,
classes{s}}{3};
191 distribution = self.server.serviceProcess{1,
class.index}{3};
194 line_warning(mfilename,
'No distribution is available for the specified class.\n');
199 function setService(self,
class, distribution, weight)
200 % SETSERVICE(CLASS, DISTRIBUTION, WEIGHT)
201 if nargin<4 %~exist(
'weight',
'var')
204 if distribution.isImmediate()
205 distribution = Immediate.getInstance();
207 if isa(class,'SelfLoopingClass') && class.refstat.index ~= self.index && ~isa(distribution,'Disabled')
208 line_error(mfilename, 'For a self-looping class, service cannot be set on stations other than the reference station of the class.');
211 server = self.server; % by reference
213 if length(server.serviceProcess) >= c && ~isempty(server.serviceProcess{1,c}) %
if the distribution was already configured
214 %
this is a forced state reset in
case for example the number of phases changes
215 % appears to run faster without checks, probably due to
217 %oldDistribution = server.serviceProcess{1, c}{3};
218 %isOldMarkovian = isa(oldDistribution,
'Markovian');
219 %isNewMarkovian = isa(distribution,
'Markovian');
220 %
if distribution.getNumParams ~= oldDistribution.getNumParams
221 % %|| (isOldMarkovian && ~isNewMarkovian) || (~isOldMarkovian && isNewMarkovian) || (isOldMarkovian && isNewMarkovian && distribution.getNumberOfPhases ~= oldDistribution.getNumberOfPhases)
222 self.model.setInitialized(
false); %
this is a better way to invalidate to avoid that sequential calls to setService all trigger an initDefault
223 self.state=[]; % reset the state vector
225 else %
if first configuration
226 if length(self.classCap) < c
227 self.classCap((length(self.classCap)+1):c) = Inf;
229 self.setStrategyParam(
class, weight);
230 % Default to Drop
for finite capacity, WaitingQueue otherwise
231 if self.cap < intmax && ~isinf(self.cap)
232 self.dropRule(c) = DropStrategy.DROP;
234 self.dropRule(c) = DropStrategy.WAITQ;
236 server.serviceProcess{1, c}{2} = ServiceStrategy.LI;
238 server.serviceProcess{1, c}{3} = distribution;
239 self.serviceProcess{c} = distribution;
241 self.obj.setService(
class.obj, distribution.obj, weight);
245 function setDelayOff(self,
jobclass, setupTime, delayoffTime)
247 self.setupTime{1, c} = setupTime;
248 self.delayoffTime{1, c} = delayoffTime;
251 function dist = getSetupTime(self,
jobclass)
253 if c <= length(self.setupTime) && ~isempty(self.setupTime{1, c})
254 dist = self.setupTime{1, c};
260 function dist = getDelayOffTime(self,
jobclass)
262 if c <= length(self.delayoffTime) && ~isempty(self.delayoffTime{1, c})
263 dist = self.delayoffTime{1, c};
269 function setSwitchover(self, varargin)
270 if isempty(self.switchoverTime)
271 if SchedStrategy.toId(self.schedStrategy) == SchedStrategy.POLLING
272 self.switchoverTime = cell(1,length(self.model.getClasses()));
274 K = length(self.model.getClasses());
275 self.switchoverTime = cell(K,K);
278 self.switchoverTime{r,s} = Immediate();
283 if length(varargin)==2
285 soTime = varargin{2};
286 % time to
switch from queue i to the next one
287 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.POLLING
288 line_error(mfilename,
'setSwitchover(jobclass, distrib) can only be invoked on queues with SchedStrategy.POLLING.\n');
291 self.switchoverTime{1,c} = soTime;
292 elseif length(varargin)==3
293 jobclass_from = varargin{1};
294 jobclass_to = varargin{2};
295 soTime = varargin{3};
296 f = jobclass_from.index;
297 t = jobclass_to.index;
298 self.switchoverTime{f,t} = soTime;
302 function setPollingType(self, rule, par)
303 if PollingType.toId(rule) ~= PollingType.KLIMITED
305 elseif PollingType.toId(rule) == PollingType.KLIMITED && nargin<3
306 line_error(mfilename,
'K-Limited polling requires to specify the parameter K, e.g., setPollingType(PollingType.KLIMITED, 2).\n');
308 % support only identical polling type at each
class buffer
309 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.POLLING
310 line_error(mfilename,
'setPollingType can only be invoked on queues with SchedStrategy.POLLING.\n');
312 for r=1:length(self.model.getClasses())
313 self.pollingType{1,r} = rule;
314 self.pollingPar = par;
315 classes = self.model.getClasses();
316 setSwitchover(self,
classes{r}, Immediate());
320 function setPatience(self,
class, varargin)
321 % SETPATIENCE(CLASS, DISTRIBUTION) - Backwards compatible
322 % SETPATIENCE(CLASS, PATIENCETYPE, DISTRIBUTION) - Explicit type
324 % Sets the patience type and distribution
for a specific job
class at this queue.
325 % Jobs that wait longer than their patience time will abandon the queue.
328 %
class - JobClass object
329 % patienceType - (Optional) PatienceType constant (RENEGING or BALKING)
330 % If omitted, defaults to PatienceType.RENEGING
331 % distribution - Any LINE distribution (Exp, Erlang, HyperExp, etc.)
332 % excluding modulated processes (BMAP, MAP, MMPP2)
334 % Note: This setting takes precedence over the global
class patience.
337 % queue.setPatience(
jobclass, Exp(0.2)) % Defaults to RENEGING
338 % queue.setPatience(
jobclass, PatienceType.RENEGING, Exp(0.2))
339 % queue.setPatience(
jobclass, PatienceType.BALKING, Exp(0.5))
341 % Handle backwards compatibility: 2 or 3 arguments
342 if length(varargin) == 1
343 % Old signature: setPatience(
class, distribution)
344 distribution = varargin{1};
345 patienceType = PatienceType.RENEGING; % Default to RENEGING
346 elseif length(varargin) == 2
347 % New signature: setPatience(
class, patienceType, distribution)
348 patienceType = varargin{1};
349 distribution = varargin{2};
351 line_error(mfilename,
'Invalid number of arguments. Use setPatience(class, distribution) or setPatience(class, patienceType, distribution)');
354 if isa(distribution,
'BMAP') || isa(distribution,
'MAP') || isa(distribution,
'MMPP2')
355 line_error(mfilename, 'Modulated processes (BMAP, MAP, MMPP2) are not supported for patience distributions.');
358 % Validate patience type
359 if patienceType ~= PatienceType.RENEGING && patienceType ~= PatienceType.BALKING
360 line_error(mfilename, 'Invalid patience type. Use PatienceType.RENEGING or PatienceType.BALKING.');
363 % Only RENEGING
is currently supported
364 if patienceType == PatienceType.BALKING
365 line_error(mfilename, 'BALKING patience type
is not yet supported. Use PatienceType.RENEGING.');
368 if distribution.isImmediate()
369 distribution = Immediate.getInstance();
374 self.patienceDistributions{1, c} = distribution;
375 self.patienceTypes{1, c} = patienceType;
377 self.obj.setPatience(
class.obj, patienceType, distribution.obj);
381 function distribution = getPatience(self,
class)
382 % DISTRIBUTION = GETPATIENCE(CLASS)
384 % Returns the patience distribution
for a specific job
class.
385 % Returns the queue-specific setting
if available, otherwise
386 % falls back to the global
class patience.
389 %
class - JobClass object
392 % distribution - The patience distribution, or []
if not set
396 % Check queue-specific patience first
397 if c <= length(self.patienceDistributions) && ~isempty(self.patienceDistributions{1, c})
398 distribution = self.patienceDistributions{1, c};
400 % Fall back to global
class patience
401 distribution =
class.getPatience();
404 distObj = self.obj.getPatience(
class.obj);
408 distribution = Distribution.fromJavaObject(distObj);
413 function patienceType = getPatienceType(self,
class)
414 % PATIENCETYPE = GETPATIENCETYPE(CLASS)
416 % Returns the patience type
for a specific job
class.
417 % Returns the queue-specific setting
if available, otherwise
418 % falls back to the global
class patience type.
421 %
class - JobClass object
424 % patienceType - The patience type (PatienceType constant), or []
if not set
428 % Check queue-specific patience type first
429 if c <= length(self.patienceTypes) && ~isempty(self.patienceTypes{1, c})
430 patienceType = self.patienceTypes{1, c};
432 % Fall back to global
class patience type
433 patienceType =
class.getPatienceType();
436 patienceTypeId = self.obj.getPatienceType(
class.obj);
437 if isempty(patienceTypeId)
440 patienceType = PatienceType.fromId(patienceTypeId.getID());
445 function tf = hasPatience(self,
class)
446 % TF = HASPATIENCE(CLASS)
448 % Returns
true if this class has patience configured at this queue
449 % (either locally or globally).
451 dist = self.getPatience(
class);
452 tf = ~isempty(dist) && ~isa(dist,
'Disabled');
455 % function distrib = getServiceProcess(self, oclass)
456 % distrib = self.serviceProcess{oclass};