1
2
3
4 """Simulation 2.1 Implements SimPy Processes, Resources, Buffers, and the backbone simulation
5 scheduling by coroutine calls. Provides data collection through classes
6 Monitor and Tally.
7 Based on generators (Python 2.3 and later; not 3.0)
8
9 LICENSE:
10 Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009, 2010 Klaus G. Muller, Tony Vignaux
11 mailto: kgmuller at xs4all.nl and Tony.Vignaux at vuw.ac.nz
12
13 This library is free software; you can redistribute it and / or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2.1 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 - 1307 USA
26 END OF LICENSE
27
28 """
29
30 import random
31 import sys
32 import types
33 from heapq import heappush, heappop
34
35 from SimPy.Lister import Lister
36 from SimPy.Recording import Monitor, Tally
37 from SimPy.Lib import Process, SimEvent, PriorityQ, Resource, Level, \
38 Store, Simerror, FatalSimerror
39
40
41 import SimPy.Globals as Globals
42 from SimPy.Globals import initialize, simulate, now, stopSimulation, \
43 allEventNotices, allEventTimes, startCollection,\
44 _startWUStepping, _stopWUStepping, activate, reactivate
45
46
47 __TESTING = False
48 version = __version__ = '2.1 $Revision: 484 $ $Date: 2010-05-10 09:18:06 +0200 (Mon, 10 May 2010) $'
49 if __TESTING:
50 print 'SimPy.Simulation %s' %__version__,
51 if __debug__:
52 print '__debug__ on'
53 else:
54 print
55
56
57 hold = 1
58 passivate = 2
59 request = 3
60 release = 4
61 waitevent = 5
62 queueevent = 6
63 waituntil = 7
64 get = 8
65 put = 9
66
67
71
72 infinity = Infinity()
73
76
78 """Handles 'yield request, self, res' and
79 'yield (request, self, res),(<code>,self, par)'.
80 <code > can be 'hold' or 'waitevent'.
81 """
82 if type(a[0][0]) == tuple:
83
84
85 b = a[0][0]
86
87
88
89 b[2]._request(arg = (b, a[1]))
90
91
92 class _Holder(Process):
93 """Provides timeout process"""
94 def __init__(self,name,sim=None):
95 Process.__init__(self,name=name,sim=sim)
96 def trigger(self, delay):
97 yield hold, self, delay
98 if not proc in b[2].activeQ:
99 proc.sim.reactivate(proc)
100
101 class _EventWait(Process):
102 """Provides event waiting process"""
103 def __init__(self,name,sim=None):
104 Process.__init__(self,name=name,sim=sim)
105 def trigger(self, event):
106 yield waitevent, self, event
107 if not proc in b[2].activeQ:
108 proc.eventsFired = self.eventsFired
109 proc.sim.reactivate(proc)
110
111
112 proc = a[0][0][1]
113 actCode = a[0][1][0]
114 if actCode == hold:
115 proc._holder = _Holder(name = 'RENEGE - hold for %s'%proc.name,
116 sim=proc.sim)
117
118 proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
119 elif actCode == waituntil:
120 raise FatalSimerror('Illegal code for reneging: waituntil')
121 elif actCode == waitevent:
122 proc._holder = _EventWait(name = 'RENEGE - waitevent for %s'\
123 %proc.name,sim=proc.sim)
124
125 proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
126 elif actCode == queueevent:
127 raise FatalSimerror('Illegal code for reneging: queueevent')
128 else:
129 raise FatalSimerror('Illegal code for reneging %s'%actCode)
130 else:
131
132 a[0][2]._request(a)
133
136
139
141
142 evtpar = a[0][2]
143 if isinstance(evtpar, SimEvent):
144 a[0][2]._wait(a)
145
146 else:
147
148 evtpar[0]._waitOR(a)
149
151
152 evtpar = a[0][2]
153 if isinstance(evtpar, SimEvent):
154 a[0][2]._queue(a)
155
156 else:
157
158 evtpar[0]._queueOR(a)
159
162
164 """Handles 'yield get, self, buffer, what, priority' and
165 'yield (get, self, buffer, what, priority),(<code>,self, par)'.
166 <code > can be 'hold' or 'waitevent'.
167 """
168 if type(a[0][0]) == tuple:
169
170
171 b = a[0][0]
172
173
174
175 b[2]._get(arg = (b, a[1]))
176
177
178 class _Holder(Process):
179 """Provides timeout process"""
180 def __init__(self,**par):
181 Process.__init__(self,**par)
182 def trigger(self, delay):
183 yield hold, self, delay
184
185 if proc in b[2].getQ:
186 a[1].sim.reactivate(proc)
187
188 class _EventWait(Process):
189 """Provides event waiting process"""
190 def __init__(self,**par):
191 Process.__init__(self,**par)
192 def trigger(self, event):
193 yield waitevent, self, event
194 if proc in b[2].getQ:
195 a[1].eventsFired = self.eventsFired
196 a[1].sim.reactivate(proc)
197
198
199 proc = a[0][0][1]
200 actCode = a[0][1][0]
201 if actCode == hold:
202 proc._holder = _Holder(name='RENEGE - hold for %s'%proc.name,
203 sim=proc.sim)
204
205 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
206 elif actCode == waituntil:
207 raise FatalSimerror('waituntil: Illegal code for reneging: waituntil')
208 elif actCode == waitevent:
209 proc._holder = _EventWait(name="RENEGE - waitevent for%s"\
210 %proc.name,sim=proc.sim)
211
212 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
213 elif actCode == queueevent:
214 raise FatalSimerror('Illegal code for reneging: queueevent')
215 else:
216 raise FatalSimerror('Illegal code for reneging %s'%actCode)
217 else:
218
219 a[0][2]._get(a)
220
221
223 """Handles 'yield put' (simple and compound hold / waitevent)
224 """
225 if type(a[0][0]) == tuple:
226
227
228 b = a[0][0]
229
230
231
232 b[2]._put(arg = (b, a[1]))
233
234
235 class _Holder(Process):
236 """Provides timeout process"""
237 def __init__(self,**par):
238 Process.__init__(self,**par)
239 def trigger(self, delay):
240 yield hold, self, delay
241
242 if proc in b[2].putQ:
243 a[1].sim.reactivate(proc)
244
245 class _EventWait(Process):
246 """Provides event waiting process"""
247 def __init__(self,**par):
248 Process.__init__(self,**par)
249 def trigger(self, event):
250 yield waitevent, self, event
251 if proc in b[2].putQ:
252 a[1].eventsFired = self.eventsFired
253 a[1].sim.reactivate(proc)
254
255
256 proc = a[0][0][1]
257 actCode = a[0][1][0]
258 if actCode == hold:
259 proc._holder = _Holder(name='RENEGE - hold for %s'%proc.name,
260 sim=proc.sim)
261
262 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
263 elif actCode == waituntil:
264 raise FatalSimerror('Illegal code for reneging: waituntil')
265 elif actCode == waitevent:
266 proc._holder = _EventWait(name='RENEGE - waitevent for %s'\
267 %proc.name,sim=proc.sim)
268
269 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
270 elif actCode == queueevent:
271 raise FatalSimerror('Illegal code for reneging: queueevent')
272 else:
273 raise FatalSimerror('Illegal code for reneging %s'%actCode)
274 else:
275
276 a[0][2]._put(a)
277
278
280 _dispatch = {
281 hold: holdfunc, request: requestfunc, release: releasefunc,
282 passivate: passivatefunc, waitevent: waitevfunc,
283 queueevent: queueevfunc, waituntil: waituntilfunc, get: getfunc,
284 put: putfunc,
285 }
286 _commandcodes = _dispatch.keys()
287 _commandwords = {
288 hold: 'hold', request: 'request', release: 'release',
289 passivate: 'passivate', waitevent: 'waitevent',
290 queueevent: 'queueevent', waituntil: 'waituntil', get: 'get',
291 put: 'put'
292 }
293
296
298 self._t = 0
299 self.next_time = 0
300
301
302 self._timestamps = []
303 self._sortpr = 0
304
305 self._start = False
306 self._stop = False
307 self.condQ = []
308 self.allMonitors = []
309 self.allTallies = []
310
313
315 """Application function to stop simulation run"""
316 self._stop = True
317
318 - def _post(self, what, at, prior = False):
319 """Post an event notice for process what for time at"""
320
321 if at < self._t:
322 raise FatalSimerror('Attempt to schedule event in the past')
323 what._nextTime = at
324 self._sortpr -= 1
325 if prior:
326
327
328
329
330 what._rec = [at, self._sortpr, what, False]
331
332 heappush(self._timestamps, what._rec)
333 else:
334
335
336 what._rec = [at,-self._sortpr, what, False]
337
338 heappush(self._timestamps, what._rec)
339
340 - def _unpost(self, whom):
341 """
342 Mark event notice for whom as cancelled if whom is a suspended process
343 """
344 if whom._nextTime is not None:
345 whom._rec[3] = True
346 whom._nextTime = None
347
349 """Returns string with eventlist as;
350 t1: processname, processname2
351 t2: processname4, processname5, . . .
352 . . . .
353 """
354 ret = ''
355 tempList = []
356 tempList[:] = self._timestamps
357 tempList.sort()
358
359 tempList = [[x[0],x[2].name] for x in tempList if not x[3]]
360 tprev = -1
361 for t in tempList:
362
363 if t[0] == tprev:
364
365 ret += ', %s'%t[1]
366 else:
367
368 if tprev == -1:
369 ret = '%s: %s' % (t[0],t[1])
370 else:
371 ret += '\n%s: %s' % (t[0],t[1])
372 tprev = t[0]
373 return ret + '\n'
374
376 """Returns list of all times for which events are scheduled.
377 """
378 r = []
379 r[:] = self._timestamps
380 r.sort()
381
382 r1 = [x[0] for x in r if not x[3]]
383 tprev = -1
384 ret = []
385 for t in r1:
386 if t == tprev:
387
388 pass
389 else:
390 ret.append(t)
391 tprev = t
392 return ret
393
394 - def activate(self, obj, process, at = 'undefined', delay = 'undefined',
395 prior = False):
396 """Application function to activate passive process."""
397 if __debug__:
398 if not (obj.sim == self):
399 txt="activate: Process %s not in activating Simulation instance"\
400 %obj.name
401 raise FatalSimerror(txt)
402 if not (type(process) == types.GeneratorType):
403 raise FatalSimerror('Activating function which'+
404 ' is not a generator (contains no \'yield\')')
405 if not obj._terminated and not obj._nextTime:
406
407 obj._nextpoint = process
408 if at == 'undefined':
409 at = self._t
410 if delay == 'undefined':
411 zeit = max(self._t, at)
412 else:
413 zeit = max(self._t, self._t + delay)
414 self._post(obj, at = zeit, prior = prior)
415
416 - def reactivate(self, obj, at = 'undefined', delay = 'undefined',
417 prior = False):
418 """Application function to reactivate a process which is active,
419 suspended or passive."""
420
421 if not obj._terminated:
422 a = Process('SimPysystem',sim=self)
423 a.cancel(obj)
424
425 if at == 'undefined':
426 at = self._t
427 if delay == 'undefined':
428 zeit = max(self._t, at)
429 else:
430 zeit = max(self._t, self._t + delay)
431 self._post(obj, at = zeit, prior = prior)
432
434 """Starts data collection of all designated Monitor and Tally objects
435 (default = all) at time 'when'.
436 """
437 class Starter(Process):
438 def collect(self, monitors, tallies):
439 for m in monitors:
440 m.reset()
441 for t in tallies:
442 t.reset()
443 yield hold, self
444 if monitors is None:
445 monitors = self.allMonitors
446 if tallies is None:
447 tallies = self.allTallies
448 if when == 0.0:
449 for m in monitors:
450 try:
451 ylast = m[-1][1]
452 empty = False
453 except IndexError:
454 empty = True
455 m.reset()
456 if not empty:
457 m.observe(t = now(), y = ylast)
458 for t in tallies:
459 t.reset()
460 else:
461 s = Starter(sim = self)
462 self.activate(s, s.collect(monitors = monitors, tallies = tallies),\
463 at = when, prior = True)
464
465
467 """
468 Puts a process 'proc' waiting for a condition into a waiting queue.
469 'cond' is a predicate function which returns True if the condition is
470 satisfied.
471 """
472 if not cond():
473 self.condQ.append(proc)
474 proc.cond = cond
475
476 proc._nextTime = None
477 else:
478
479 self._post(proc, at = self._t, prior = 1)
480
482 """Marks a process as terminated."""
483 process._nextpoint = None
484 process._terminated = True
485 process._nextTime = None
486
488 """
489 Checks if there are events which can be processed. Returns ``True`` if
490 there are events and the simulation has not been stopped.
491 """
492 return not self._stop and self._timestamps
493
495 """
496 Returns the time of the next event or infinity, if no
497 more events are scheduled.
498 """
499 if not self._timestamps:
500 return infinity
501 else:
502 return self._timestamps[0][0]
503
505 """
506 Executes the next uncancelled event in the eventqueue.
507 """
508
509
510 noActiveNotice = True
511
512 while noActiveNotice:
513 if self._timestamps:
514 _tnotice, p, proc, cancelled = heappop(self._timestamps)
515 noActiveNotice = cancelled
516 else:
517 return None
518
519
520 proc._rec = None
521 self._t = _tnotice
522
523
524 try:
525 resultTuple = proc._nextpoint.next()
526
527
528
529 if type(resultTuple[0]) == tuple:
530
531
532 command = resultTuple[0][0]
533 else:
534 command = resultTuple[0]
535 if __debug__:
536 if not command in self._commandcodes:
537 raise FatalSimerror('Illegal command: yield %s'%command)
538 self._dispatch[command]((resultTuple, proc))
539 except StopIteration:
540
541 self._terminate(proc)
542
543
544
545
546
547
548
549 if self.condQ:
550 i = 0
551 while i < len(self.condQ):
552 proc = self.condQ[i]
553 if proc.cond():
554 self.condQ.pop(i)
555 self.reactivate(proc)
556 else:
557 i += 1
558
559
560
561 if self._timestamps:
562 return self._timestamps[0][0]
563 else:
564 return None
565
567 """
568 Start the simulation and run its loop until the timeout ``until`` is
569 reached, stopSimulation is called, or no more events are scheduled.
570 """
571 try:
572 if not self._timestamps:
573 return 'SimPy: No activities scheduled'
574
575
576
577
578 step = self.step
579 timestamps = self._timestamps
580 while not self._stop and timestamps and timestamps[0][0] <= until:
581 step()
582
583 if not self._stop and timestamps:
584
585 self._t = until
586 return 'SimPy: Normal exit at time %s' % self._t
587 elif not timestamps:
588
589 return 'SimPy: No more events at time %s' % self._t
590 else:
591
592 return 'SimPy: Run stopped at time %s' % self._t
593 except FatalSimerror, error:
594 print 'SimPy: ' + error.value
595 raise FatalSimerror, 'SimPy: ' + error.value
596 except Simerror, error:
597 return 'SimPy: ' + error.value
598 finally:
599 self._stop = True
600
601
602 Globals.sim = Simulation()
603
604 peek = Globals.sim.peek
605
606 step = Globals.sim.step
607
608
609 if __name__ == '__main__':
610 print 'SimPy.Simulation %s' %__version__
611
613 class Aa(Process):
614 sequIn = []
615 sequOut = []
616 def __init__(self, holdtime, name,sim=None):
617 Process.__init__(self, name,sim=sim)
618 self.holdtime = holdtime
619
620 def life(self, priority):
621 for i in range(1):
622 Aa.sequIn.append(self.name)
623 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
624 len(rrr.activeQ)
625 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ]
626 print 'activeQ: ',[(k.name, k._priority[rrr]) \
627 for k in rrr.activeQ]
628 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
629 'Inconsistent resource unit numbers'
630 print self.sim.now(),self.name, 'requests 1 ', rrr.unitName
631 yield request, self, rrr, priority
632 print self.sim.now(),self.name, 'has 1 ', rrr.unitName
633 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
634 len(rrr.activeQ)
635 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
636 len(rrr.activeQ)
637 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
638 'Inconsistent resource unit numbers'
639 yield hold, self, self.holdtime
640 print self.sim.now(),self.name, 'gives up 1', rrr.unitName
641 yield release, self, rrr
642 Aa.sequOut.append(self.name)
643 print self.sim.now(),self.name, 'has released 1 ', rrr.unitName
644 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ]
645 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
646 len(rrr.activeQ)
647 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
648 'Inconsistent resource unit numbers'
649
650 class Observer(Process):
651 def __init__(self,**vars):
652 Process.__init__(self,**vars)
653
654 def observe(self, step, processes, res):
655 while self.sim.now() < 11:
656 for i in processes:
657 print '++ %s process: %s: active:%s, passive:%s, terminated: %s, interrupted:%s, queuing:%s'\
658 %(self.sim.now(),i.name, i.active(),i.passive(),\
659 i.terminated(),i.interrupted(),i.queuing(res))
660 print
661 yield hold, self, step
662
663 print'\n+++test_demo output'
664 print '****First case == priority queue, resource service not preemptable'
665 s=Simulation()
666 s.initialize()
667 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ,
668 preemptable = 0,sim=s)
669 procs = []
670 for i in range(10):
671 z = Aa(holdtime = i, name = 'Car ' + str(i),sim=s)
672 procs.append(z)
673 s.activate(z, z.life(priority = i))
674 o = Observer(sim=s)
675 s.activate(o, o.observe(1, procs, rrr))
676 a = s.simulate(until = 10000)
677 print a
678 print 'Input sequence: ', Aa.sequIn
679 print 'Output sequence: ', Aa.sequOut
680
681 print '\n****Second case == priority queue, resource service preemptable'
682 s=Simulation()
683 s.initialize()
684 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ,
685 preemptable = 1,sim=s)
686 procs = []
687 for i in range(10):
688 z = Aa(holdtime = i, name = 'Car ' + str(i),sim=s)
689 procs.append(z)
690 s.activate(z, z.life(priority = i))
691 o = Observer(sim=s)
692 s.activate(o, o.observe(1, procs, rrr))
693 Aa.sequIn = []
694 Aa.sequOut = []
695 a = s.simulate(until = 10000)
696 print a
697 print 'Input sequence: ', Aa.sequIn
698 print 'Output sequence: ', Aa.sequOut
699
701 class Bus(Process):
702 def __init__(self, **vars):
703 Process.__init__(self, **vars)
704
705 def operate(self, repairduration = 0):
706 print self.sim.now(),'>> %s starts' % (self.name)
707 tripleft = 1000
708 while tripleft > 0:
709 yield hold, self, tripleft
710 if self.interrupted():
711 print 'interrupted by %s' %self.interruptCause.name
712 print '%s: %s breaks down ' %(now(),self.name)
713 tripleft = self.interruptLeft
714 self.interruptReset()
715 print 'tripleft ', tripleft
716 s.reactivate(br, delay = repairduration)
717 yield hold, self, repairduration
718 print self.sim.now(),' repaired'
719 else:
720 break
721 print self.sim.now(),'<< %s done' % (self.name)
722
723 class Breakdown(Process):
724 def __init__(self, myBus,sim=None):
725 Process.__init__(self, name = 'Breakdown ' + myBus.name,sim=sim)
726 self.bus = myBus
727
728 def breakBus(self, interval):
729
730 while True:
731 yield hold, self, interval
732 if self.bus.terminated(): break
733 self.interrupt(self.bus)
734
735 print'\n\n+++test_interrupt'
736 s=Simulation()
737 s.initialize()
738 b = Bus(name='Bus 1',sim=s)
739 s.activate(b, b.operate(repairduration = 20))
740 br = Breakdown(b,sim=s)
741 s.activate(br, br.breakBus(200))
742 print s.simulate(until = 4000)
743
745 class Waiter(Process):
746 def __init__(self,**vars):
747 Process.__init__(self,**vars)
748 def waiting(self, theSignal):
749 while True:
750 yield waitevent, self, theSignal
751 print '%s: process \'%s\' continued after waiting for %s' %\
752 (self.sim.now(),self.name, theSignal.name)
753 yield queueevent, self, theSignal
754 print '%s: process \'%s\' continued after queueing for %s' % (now(),self.name, theSignal.name)
755
756 class ORWaiter(Process):
757 def __init__(self,**vars):
758 Process.__init__(self,**vars)
759 def waiting(self, signals):
760 while True:
761 yield waitevent, self, signals
762 print self.sim.now(),'one of %s signals occurred' %\
763 [x.name for x in signals]
764 print '\t%s (fired / param)'%\
765 [(x.name, x.signalparam) for x in self.eventsFired]
766 yield hold, self, 1
767
768 class Caller(Process):
769 def __init__(self,**vars):
770 Process.__init__(self,**vars)
771 def calling(self):
772 while True:
773 signal1.signal('wake up!')
774 print '%s: signal 1 has occurred'%now()
775 yield hold, self, 10
776 signal2.signal('and again')
777 signal2.signal('sig 2 again')
778 print '%s: signal1, signal2 have occurred'%now()
779 yield hold, self, 10
780 print'\n+++testSimEvents output'
781 s=Simulation()
782 s.initialize()
783 signal1 = SimEvent('signal 1',sim=s)
784 signal2 = SimEvent('signal 2',sim=s)
785 signal1.signal('startup1')
786 signal2.signal('startup2')
787 w1 = Waiter(name='waiting for signal 1',sim=s)
788 s.activate(w1, w1.waiting(signal1))
789 w2 = Waiter(name='waiting for signal 2',sim=s)
790 s.activate(w2, w2.waiting(signal2))
791 w3 = Waiter(name='also waiting for signal 2',sim=s)
792 s.activate(w3, w3.waiting(signal2))
793 w4 = ORWaiter(name='waiting for either signal 1 or signal 2',sim=s)
794 s.activate(w4, w4.waiting([signal1, signal2]),prior = True)
795 c = Caller(name='Caller',sim=s)
796 s.activate(c, c.calling())
797 print s.simulate(until = 100)
798
800 """
801 Demo of waitUntil capability.
802
803 Scenario:
804 Three workers require sets of tools to do their jobs. Tools are shared,
805 scarce resources for which they compete.
806 """
807 class Worker(Process):
808 def __init__(self, name, heNeeds = [],sim=None):
809 Process.__init__(self, name,sim=sim)
810 self.heNeeds = heNeeds
811 def work(self):
812 def workerNeeds():
813 for item in self.heNeeds:
814 if item.n == 0:
815 return False
816 return True
817
818 while self.sim.now() < 8 * 60:
819 yield waituntil, self, workerNeeds
820 for item in self.heNeeds:
821 yield request, self, item
822 print '%s %s has %s and starts job' % (self.sim.now(),self.name,
823 [x.name for x in self.heNeeds])
824 yield hold, self, random.uniform(10, 30)
825 for item in self.heNeeds:
826 yield release, self, item
827 yield hold, self, 2
828
829 print '\n+++ nwaituntil demo output'
830 random.seed(12345)
831 s=Simulation()
832 s.initialize()
833 brush = Resource(capacity = 1, name = 'brush',sim=s)
834 ladder = Resource(capacity = 2, name = 'ladder',sim=s)
835 hammer = Resource(capacity = 1, name = 'hammer',sim=s)
836 saw = Resource(capacity = 1, name = 'saw',sim=s)
837 painter = Worker('painter',[brush, ladder],sim=s)
838 s.activate(painter, painter.work())
839 roofer = Worker('roofer',[hammer, ladder, ladder],sim=s)
840 s.activate(roofer, roofer.work())
841 treeguy = Worker('treeguy',[saw, ladder],sim=s)
842 s.activate(treeguy, treeguy.work())
843 for who in (painter, roofer, treeguy):
844 print '%s needs %s for his job' %\
845 (who.name,[x.name for x in who.heNeeds])
846 print
847 print s.simulate(until = 9 * 60)
848
849
850
851
852
853
854
855
856
857
859 """ Job class for testing timeout reneging
860 """
861 - def __init__(self, server = None, name = '',sim=None):
865
866 - def execute(self, timeout, usetime):
867 yield (request, self, self.res),(hold, self, timeout)
868 if self.acquired(self.res):
869 self.gotResource = True
870 yield hold, self, usetime
871 yield release, self, self.res
872 else:
873 self.gotResource = False
874
875
877 """Test that resource gets acquired without timeout
878 """
879 s=Simulation()
880 s.initialize()
881 res = Resource(name = 'Server', capacity = 1,sim=s)
882 usetime = 5
883 timeout = 1000000
884 j1 = JobTO(server = res, name = 'Job_1',sim=s)
885 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime))
886 j2 = JobTO(server = res, name = 'Job_2',sim=s)
887 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime))
888 s.simulate(until = 2 * usetime)
889 assert s.now() == 2 * usetime, 'time not == 2 * usetime'
890 assert j1.gotResource and j2.gotResource,\
891 'at least one job failed to get resource'
892 assert not (res.waitQ or res.activeQ),\
893 'job waiting or using resource'
894
896 """Test that timeout occurs when resource busy
897 """
898 s=Simulation()
899 s.initialize()
900 res = Resource(name = 'Server', capacity = 1, monitored = True,sim=s)
901 usetime = 5
902 timeout = 3
903 j1 = JobTO(server = res, name = 'Job_1',sim=s)
904 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime))
905 j2 = JobTO(server = res, name = 'Job_2',sim=s)
906 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime))
907 s.simulate(until = 2 * usetime)
908 assert(s.now() == usetime),'time not == usetime'
909 assert(j1.gotResource),'Job_1 did not get resource'
910 assert(not j2.gotResource),'Job_2 did not renege'
911 assert not (res.waitQ or res.activeQ),\
912 'job waiting or using resource'
913
915 """Test that timeout occurs when resource has no capacity free
916 """
917 s=Simulation()
918 s.initialize()
919 res = Resource(name = 'Server', capacity = 0,sim=s)
920 usetime = 5
921 timeout = 3
922 j1 = JobTO(server = res, name = 'Job_1',sim=s)
923 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime))
924 j2 = JobTO(server = res, name = 'Job_2',sim=s)
925 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime))
926 s.simulate(until = 2 * usetime)
927 assert s.now() == timeout, 'time %s not == timeout'%now()
928 assert not j1.gotResource, 'Job_1 got resource'
929 assert not j2.gotResource, 'Job_2 got resource'
930 assert not (res.waitQ or res.activeQ),\
931 'job waiting or using resource'
932
933
934
935
936
938 """ Job class for testing event reneging
939 """
940 - def __init__(self, server = None, name = '',sim=None):
944
945 - def execute(self, event, usetime):
946 yield (request, self, self.res),(waitevent, self, event)
947 if self.acquired(self.res):
948 self.gotResource = True
949 yield hold, self, usetime
950 yield release, self, self.res
951 else:
952 self.gotResource = False
953
955 """ Job class for testing event reneging with multi - event lists
956 """
957 - def __init__(self, server = None, name = '',sim=None):
961
962 - def execute(self, eventlist, usetime):
963 yield (request, self, self.res),(waitevent, self, eventlist)
964 if self.acquired(self.res):
965 self.gotResource = True
966 yield hold, self, usetime
967 yield release, self, self.res
968 else:
969 self.gotResource = False
970
972 """Fires reneging event
973 """
976 - def fire(self, fireDelay, event):
977 yield hold, self, fireDelay
978 event.signal()
979
981 """Test that processes acquire resource normally if no event fires
982 """
983 s=Simulation()
984 s.initialize()
985 res = Resource(name = 'Server', capacity = 1,sim=s)
986 event = SimEvent(name='Renege_trigger',sim=s)
987 usetime = 5
988 j1 = JobEvt(server = res, name = 'Job_1',sim=s)
989 s.activate(j1, j1.execute(event = event, usetime = usetime))
990 j2 = JobEvt(server = res, name = 'Job_2',sim=s)
991 s.activate(j2, j2.execute(event = event, usetime = usetime))
992 s.simulate(until = 2 * usetime)
993
994 assert s.now() == 2 * usetime, 'time not == 2 * usetime'
995 assert j1.gotResource and j2.gotResource,\
996 'at least one job failed to get resource'
997 assert not (res.waitQ or res.activeQ),\
998 'job waiting or using resource'
999
1001 """Test that signalled event leads to renege when resource busy
1002 """
1003 s=Simulation()
1004 s.initialize()
1005 res = Resource(name = 'Server', capacity = 1,sim=s)
1006 event = SimEvent('Renege_trigger',sim=s)
1007 usetime = 5
1008 eventtime = 1
1009 j1 = JobEvt(server = res, name = 'Job_1',sim=s)
1010 s.activate(j1, j1.execute(event = event, usetime = usetime))
1011 j2 = JobEvt(server = res, name = 'Job_2',sim=s)
1012 s.activate(j2, j2.execute(event = event, usetime = usetime))
1013 f = FireEvent(name = 'FireEvent',sim=s)
1014 s.activate(f, f.fire(fireDelay = eventtime, event = event))
1015 s.simulate(until = 2 * usetime)
1016
1017 assert(s.now() == usetime),'time not == usetime'
1018 assert(j1.gotResource),'Job_1 did not get resource'
1019 assert(not j2.gotResource),'Job_2 did not renege'
1020 assert not (res.waitQ or res.activeQ),\
1021 'job waiting or using resource'
1022
1024 """Test that renege - triggering event can be one of an event list
1025 """
1026 s=Simulation()
1027 s.initialize()
1028 res = Resource(name = 'Server', capacity = 1,sim=s)
1029 event1 = SimEvent('Renege_trigger_1',sim=s)
1030 event2 = SimEvent('Renege_trigger_2',sim=s)
1031 usetime = 5
1032 eventtime = 1
1033 j1 = JobEvtMulti(server = res, name = 'Job_1',sim=s)
1034 s.activate(j1, j1.execute(eventlist = [event1, event2],usetime = usetime))
1035 j2 = JobEvtMulti(server = res, name = 'Job_2',sim=s)
1036 s.activate(j2, j2.execute(eventlist = [event1, event2],usetime = usetime))
1037 f1 = FireEvent(name = 'FireEvent_1',sim=s)
1038 s.activate(f1, f1.fire(fireDelay = eventtime, event = event1))
1039 f2 = FireEvent(name = 'FireEvent_2',sim=s)
1040 s.activate(f2, f2.fire(fireDelay = eventtime, event = event2))
1041 s.simulate(until = 2 * usetime)
1042
1043 assert(s.now() == usetime),'time not == usetime'
1044 assert(j1.gotResource),'Job_1 did not get resource'
1045 assert(not j2.gotResource),'Job_2 did not renege'
1046 assert not (res.waitQ or res.activeQ),\
1047 'job waiting or using resource'
1048
1049 testNoTimeout()
1050 testTimeout1()
1051 testTimeout2()
1052 testNoEvent()
1053 testWaitEvent1()
1054 testWaitEvent2()
1055 test_demo()
1056 test_interrupt()
1057 testSimEvents()
1058 testwaituntil()
1059