Package SimPy :: Module Simulation
[hide private]
[frames] | no frames]

Source Code for Module SimPy.Simulation

   1  #!/usr / bin / env python 
   2  # $Revision: 163 $ $Date: 2008-09-10 17:25:13 +0200 (Mi, 10 Sep 2008)  
   3  """Simulation 2.0 Implements SimPy Processes, Resources, Buffers, and the backbone simulation 
   4  scheduling by coroutine calls. Provides data collection through classes  
   5  Monitor and Tally. 
   6  Based on generators (Python 2.3 and later; not 3.0) 
   7   
   8  LICENSE: 
   9  Copyright (C) 2002, 2005, 2006, 2007, 2008  Klaus G. Muller, Tony Vignaux 
  10  mailto: kgmuller@xs4all.nl and Tony.Vignaux@vuw.ac.nz 
  11   
  12      This library is free software; you can redistribute it and / or 
  13      modify it under the terms of the GNU Lesser General Public 
  14      License as published by the Free Software Foundation; either 
  15      version 2.1 of the License, or (at your option) any later version. 
  16   
  17      This library is distributed in the hope that it will be useful, 
  18      but WITHOUT ANY WARRANTY; without even the implied warranty of 
  19      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  20      Lesser General Public License for more details. 
  21   
  22      You should have received a copy of the GNU Lesser General Public 
  23      License along with this library; if not, write to the Free Software 
  24      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111 - 1307  USA 
  25  END OF LICENSE 
  26   
  27  **Change history:** 
  28   
  29      Started out as SiPy 0.9 
  30       
  31      5 / 9/2002: SiPy 0.9.1 
  32       
  33          - Addition of '_cancel' method in class Process and supporting '_unpost' method in  
  34            class _Evlist. 
  35           
  36          - Removal of redundant 'Action' method in class Process. 
  37           
  38      12 / 9/2002: 
  39       
  40          - Addition of resource class 
  41           
  42          - Addition of '_request' and '_release' coroutine calls 
  43           
  44      15 / 9/2002: moved into SimPy package 
  45       
  46      16 / 9/2002: 
  47          - Resource attributes fully implemented (resources can now have more 
  48            than 1 shareable resource units) 
  49           
  50      17 / 9/2002: 
  51       
  52          - corrected removal from waitQ (Vignaux) 
  53           
  54      17 / 9/2002: 
  55       
  56          - added test for queue discipline in 'test_demo()'. Must be FIFO 
  57           
  58      26 / 9/02: Version 0.2.0 
  59       
  60          - cleaned up code; more consistent naming 
  61           
  62          - prefixed all Simulation - private variable names with '_'. 
  63           
  64          - prefixed all class - private variable names with '__'. 
  65           
  66          - made normal exit quiet (but return message from scheduler() 
  67           
  68      28 / 9/02: 
  69       
  70          - included stopSimulation() 
  71           
  72      15 / 10 / 02: Simulation version 0.3 
  73       
  74          - Version printout now only if __TESTING 
  75           
  76          - '_stop' initialized to True by module load, and set to False in  
  77        initialize() 
  78           
  79          - Introduced 'simulate(until = 0)' instead of 'scheduler(till = 0)'.  
  80        Left 'scheduler()' in for backward compatibility, but marked 
  81        as deprecated. 
  82           
  83          - Added attribute 'name' to class Process; default == 'a_process' 
  84           
  85          - Changed Resource constructor to  
  86        'def __init__(self, capacity = 1, name = 'a_resource', unitName = 'units''. 
  87           
  88      13 / 11 / 02: Simulation version 0.6 
  89       
  90          - Major changes to class Resource: 
  91           
  92              - Added two queue types for resources, FIFO (default) and PriorityQ 
  93               
  94              - Changed constructor to allow selection of queue type. 
  95               
  96              - Introduced preemption of resources (to be used with PriorityQ 
  97                queue type) 
  98               
  99              - Changed constructor of class Resource to allow selection of preemption 
 100               
 101              - Changes to class Process to support preemption of service 
 102               
 103              - Cleaned up 'simulate' by replacing series of if - statements by dispatch table. 
 104   
 105      19 / 11 / 02: Simulation version 0.6.1 
 106          - Changed priority schemes so that higher values of Process  
 107            attribute 'priority' represent higher priority. 
 108   
 109      20 / 11 / 02: Simulation version 0.7 
 110          - Major change of priority approach: 
 111   
 112              - Priority set by 'yield request, self, res, priority' 
 113   
 114              - Priority of a Process instance associated with a specific  
 115                resource 
 116   
 117      25 / 11 / 02: Simulation version 0.7.1 
 118   
 119          - Code cleanup and optimization 
 120   
 121          - Made process attributes remainService and preempted private  
 122           (_remainService and _preempted) 
 123   
 124      11 / 12 / 2002: First process interrupt implementation 
 125   
 126          - Addition of user methods 'interrupt' and 'resume' 
 127   
 128          - Significant code cleanup to maintain process state 
 129   
 130      20 / 12 / 2002: Changes to 'interrupt'; addition of boolean methods to show 
 131                       process states 
 132   
 133      16 / 3/2003: Changed hold (allowing posting events past _endtime) 
 134       
 135      18 / 3/2003: Changed _nextev to prevent _t going past _endtime 
 136   
 137      23 / 3/2003: Introduced new interrupt construct; deleted 'resume' method 
 138       
 139      25 / 3/2003: Expanded interrupt construct: 
 140       
 141          - Made 'interrupt' a method  of Process 
 142   
 143          - Added 'interruptCause' as an attribute of an interrupted process 
 144   
 145          - Changed definition of 'active' to  
 146           'self._nextTime <> None and not self._inInterrupt' 
 147   
 148          - Cleaned up test_interrupt function 
 149   
 150      30 / 3/2003: Modification of 'simulate': 
 151   
 152          - error message if 'initialize' not called (fatal) 
 153   
 154          - error message if no process scheduled (warning) 
 155   
 156          - Ensured that upon exit from 'simulate', now() == _endtime is  
 157            always valid 
 158   
 159      2 / 04 / 2003: 
 160   
 161          - Modification of 'simulate': leave _endtime alone (undid change 
 162            of 30 Mar 03) 
 163   
 164          - faster '_unpost' 
 165   
 166      3 / 04 / 2003: Made 'priority' private ('_priority') 
 167   
 168      4 / 04 / 2003: Catch activation of non - generator error 
 169   
 170      5 / 04 / 2003: Added 'interruptReset()' function to Process. 
 171   
 172      7 / 04 / 2003: Changed '_unpost' to ensure that process has 
 173                     _nextTime == None (is passive) afterwards. 
 174   
 175      8 / 04 / 2003: Changed _hold to allow for 'yield hold, self'  
 176                     (equiv to 'yield hold, self, 0') 
 177   
 178      10 / 04 / 2003: Changed 'cancel' syntax to 'Process().cancel(victim)' 
 179   
 180      12 / 5/2003: Changed eventlist handling from dictionary to bisect 
 181       
 182      9 / 6/2003: - Changed eventlist handling from pure dictionary to bisect- 
 183                  sorted 'timestamps' list of keys, resulting in greatly  
 184                  improved performance for models with large 
 185                  numbers of event notices with differing event times. 
 186                  ========================================================= 
 187                  This great change was suggested by Prof. Simon Frost.  
 188                  Thank you, Simon! This version 1.3 is dedicated to you! 
 189                  ========================================================= 
 190                - Added import of Lister which supports well - structured  
 191                  printing of all attributes of Process and Resource instances. 
 192   
 193      Oct 2003: Added monitored Resource instances (Monitors for activeQ and waitQ) 
 194   
 195      13 Dec 2003: Merged in Monitor and Histogram 
 196   
 197      27 Feb 2004: Repaired bug in activeQ monitor of class Resource. Now actMon 
 198                   correctly records departures from activeQ. 
 199                    
 200      19 May 2004: Added erroneously omitted Histogram class. 
 201   
 202      5 Sep 2004: Added SimEvents synchronization constructs 
 203       
 204      17 Sep 2004: Added waituntil synchronization construct 
 205       
 206      01 Dec 2004: SimPy version 1.5 
 207                   Changes in this module: Repaired SimEvents bug re proc.eventsFired 
 208                    
 209      12 Jan 2005: SimPy version 1.5.1 
 210                   Changes in this module: Monitor objects now have a default name 
 211                                           'a_Monitor' 
 212                                            
 213      29 Mar 2005: Start SimPy 1.6: compound 'yield request' statements 
 214       
 215      05 Jun 2005: Fixed bug in _request method -- waitMon did not work properly in 
 216                   preemption case 
 217                    
 218      09 Jun 2005: Added test in 'activate' to see whether 'initialize()' was called first. 
 219       
 220      23 Aug 2005: - Added Tally data collection class 
 221                   - Adjusted Resource to work with Tally 
 222                   - Redid function allEventNotices() (returns prettyprinted string with event 
 223                     times and names of process instances 
 224                   - Added function allEventTimes (returns event times of all scheduled events) 
 225                    
 226      16 Mar 2006: - Added Store and Level classes 
 227                   - Added 'yield get' and 'yield put' 
 228                    
 229      10 May 2006: - Repaired bug in Store._get method 
 230                   - Repaired Level to allow initialBuffered have float value 
 231                   - Added type test for Level get parameter 'nrToGet' 
 232                    
 233      06 Jun 2006: - To improve pretty - printed output of 'Level' objects, changed attribute 
 234                     _nrBuffered to nrBuffered (synonym for amount property) 
 235                   - To improve pretty - printed output of 'Store' objects, added attribute 
 236                     buffered (which refers to _theBuffer) 
 237                      
 238      25 Aug 2006: - Start of version 1.8 
 239                   - made 'version' public 
 240                   - corrected condQ initialization bug 
 241                    
 242      30 Sep 2006: - Introduced checks to ensure capacity of a Buffer > 0 
 243                   - Removed from __future__ import (so Python 2.3 or later needed) 
 244                   
 245      15 Oct 2006: - Added code to register all Monitors and all Tallies in variables 
 246                     'allMonitors' and 'allTallies' 
 247                   - Added function 'startCollection' to activate Monitors and Tallies at a 
 248                     specified time (e.g. after warmup period) 
 249                   - Moved all test / demo programs to after 'if __name__ == '__main__':'. 
 250                   
 251      17 Oct 2006: - Added compound 'put' and 'get' statements for Level and Store. 
 252       
 253      18 Oct 2006: - Repaired bug: self.eventsFired now gets set after an event fires 
 254                     in a compound yield get / put with a waitevent clause (reneging case). 
 255                      
 256      21 Oct 2006: - Introduced Store 'yield get' with a filter function. 
 257                   
 258      22 Oct 2006: - Repaired bug in prettyprinting of Store objects (the buffer  
 259                     content==._theBuffer was not shown) by changing ._theBuffer  
 260                     to .theBuffer. 
 261                   
 262      04 Dec 2006: - Added printHistogram method to Tally and Monitor (generates 
 263                     table - form histogram) 
 264                       
 265      07 Dec 2006: - Changed the __str__ method of Histogram to print a table  
 266                     (like printHistogram). 
 267       
 268      18 Dec 2006: - Added trace printing of Buffers' 'unitName' for yield get and put. 
 269       
 270      09 Jun 2007: - Cleaned out all uses of 'object' to prevent name clash. 
 271       
 272      18 Nov 2007: - Start of 1.9 development 
 273                   - Added 'start' method (alternative to activate) to Process 
 274                    
 275      22 Nov 2007: - Major change to event list handling to speed up larger models: 
 276                      * Drop dictionary 
 277                      * Replace bisect by heapq 
 278                      * Mark cancelled event notices in unpost and skip them in 
 279                        nextev (great idea of Tony Vignaux)) 
 280                         
 281      4 Dec 2007: - Added twVariance calculation for both Monitor and Tally (gav) 
 282       
 283      5 Dec 2007: - Changed name back to timeVariance (gav) 
 284       
 285      1 Mar 2008: - Start of 1.9.1 bugfix release 
 286                  - Delete circular reference in Process instances when event  
 287                    notice has been processed (caused much circular garbage) 
 288                  - Added capability for multiple preempts of a process 
 289                   
 290      10 Aug 2008: - Introduced a Simulation class that now contains all global 
 291                     functions for the simulation. 
 292                      * Globals.py contains dummies for them to obtain backward 
 293                        compatibility (Ontje Luensdorf) 
 294                   - Histogram, Monitor and Tally were moved to Recording.py 
 295                     (Stefan Scherfke) 
 296                   - Process, SimEvent, Queue, FIFO, PriorityQ, Resource, Buffer, 
 297                     Level, Store moved to Resources.py 
 298                   - Renamed __Evlist to Evlist 
 299       
 300  """ 
 301   
 302  import heapq as hq 
 303  import random 
 304  import sys 
 305  import types 
 306   
 307  from SimPy.Lister import Lister 
 308  from SimPy.Recording import Monitor, Tally 
 309  from SimPy.Lib import Process, SimEvent, PriorityQ, Resource, Level, Store 
 310   
 311  # Required for backward compatibility 
 312  import SimPy.Globals as Globals 
 313  from SimPy.Globals import initialize, simulate, now, stopSimulation, \ 
 314          _startWUStepping, _stopWUStepping, activate, reactivate 
 315   
 316   
 317  __TESTING = False 
 318  version = __version__ = '2.0 $Revision: 163 $ $Date: 2008-12-15 12:47:44 +0100 (Mo, 15 Dez 2008) $' 
 319  if __TESTING:  
 320      print 'SimPy.Simulation %s' %__version__, 
 321      if __debug__: 
 322          print '__debug__ on' 
 323      else: 
 324          print 
 325   
 326  # yield keywords 
 327  hold = 1 
 328  passivate = 2 
 329  request = 3 
 330  release = 4 
 331  waitevent = 5 
 332  queueevent = 6 
 333  waituntil = 7 
 334  get = 8 
 335  put = 9 
 336   
 337   
338 -class Simerror(Exception):
339 - def __init__(self, value):
340 self.value = value
341
342 - def __str__(self):
343 return `self.value`
344 345
346 -class FatalSimerror(Simerror):
347 - def __init__(self, value):
348 Simerror.__init__(self, value) 349 self.value = value
350
351 -class Evlist(object):
352 """Defines event list and operations on it"""
353 - def __init__(self, sim):
354 # always sorted list of events (sorted by time, priority) 355 # make heapq 356 self.sim = sim 357 self.timestamps = [] 358 self.sortpr = 0
359
360 - def _post(self, what, at, prior = False):
361 """Post an event notice for process what for time at""" 362 # event notices are Process instances 363 if at < self.sim._t: 364 raise Simerror('Attempt to schedule event in the past') 365 what._nextTime = at 366 self.sortpr -= 1 367 if prior: 368 # before all other event notices at this time 369 # heappush with highest priority value so far (negative of 370 # monotonely decreasing number) 371 # store event notice in process instance 372 what._rec = [at, self.sortpr, what, False] 373 # make event list refer to it 374 hq.heappush(self.timestamps, what._rec) 375 else: 376 # heappush with lowest priority 377 # store event notice in process instance 378 what._rec = [at,-self.sortpr, what, False] 379 # make event list refer to it 380 hq.heappush(self.timestamps, what._rec)
381
382 - def _unpost(self, whom):
383 """ 384 Mark event notice for whom as cancelled if whom is a suspended process 385 """ 386 if whom._nextTime is not None: # check if whom was actually active 387 whom._rec[3] = True ## Mark as cancelled 388 whom._nextTime = None
389
390 - def _nextev(self):
391 """Retrieve next event from event list""" 392 noActiveNotice = True 393 ## Find next event notice which is not marked cancelled 394 while noActiveNotice: 395 if self.timestamps: 396 ## ignore priority value 397 (_tnotice, p, nextEvent, cancelled) = hq.heappop(self.timestamps) 398 noActiveNotice = cancelled 399 else: 400 raise Simerror('No more events at time %s' % self.sim._t) 401 nextEvent._rec = None 402 self.sim._t = _tnotice 403 if self.sim._t > self.sim._endtime: 404 self.sim._t = self.sim._endtime 405 self.sim._stop = True 406 return (None,) 407 try: 408 resultTuple = nextEvent._nextpoint.next() 409 except StopIteration: 410 nextEvent._nextpoint = None 411 nextEvent._terminated = True 412 nextEvent._nextTime = None 413 resultTuple = None 414 return (resultTuple, nextEvent)
415
416 - def _isEmpty(self):
417 return not self.timestamps
418
419 - def _allEventNotices(self):
420 """Returns string with eventlist as 421 t1: [procname, procname2] 422 t2: [procname4, procname5, . . . ] 423 . . . . 424 """ 425 ret = '' 426 for t in self.timestamps: 427 ret += '%s:%s\n' % (t[1]._nextTime, t[1].name) 428 return ret[:-1]
429
430 - def _allEventTimes(self):
431 """Returns list of all times for which events are scheduled. 432 """ 433 return self.timestamps
434 435
436 -class Simulation(object):
437 - def __init__(self):
438 self.initialize()
439
440 - def initialize(self):
441 self._endtime = 0 442 self._t = 0 443 self._e = Evlist(self) 444 self._stop = False 445 self._wustep = False #controls per event stepping for waituntil construct; not user API 446 self.condQ = [] 447 self.allMonitors = [] 448 self.allTallies = []
449
450 - def now(self):
451 return self._t
452
453 - def stopSimulation(self):
454 """Application function to stop simulation run""" 455 self._stop = True
456
457 - def _startWUStepping(self):
458 """Application function to start stepping through simulation for waituntil construct.""" 459 self._wustep = True
460
461 - def _stopWUStepping(self):
462 """Application function to stop stepping through simulation.""" 463 self._wustep = False
464
465 - def allEventNotices(self):
466 """Returns string with eventlist as; 467 t1: processname, processname2 468 t2: processname4, processname5, . . . 469 . . . . 470 """ 471 ret = '' 472 tempList = [] 473 tempList[:] = self._e.timestamps 474 tempList.sort() 475 # return only event notices which are not cancelled 476 tempList = [[x[0],x[2].name] for x in tempList if not x[3]] 477 tprev=-1 478 for t in tempList: 479 # if new time, new line 480 if t[0] == tprev: 481 # continue line 482 ret += ',%s'%t[1] 483 else: 484 # new time 485 if tprev==-1: 486 ret = '%s: %s' % (t[0],t[1]) 487 else: 488 ret += '\n%s: %s' % (t[0],t[1]) 489 tprev = t[0] 490 return ret + '\n'
491
492 - def allEventTimes(self):
493 """Returns list of all times for which events are scheduled. 494 """ 495 r = [] 496 r[:] = self._e.timestamps 497 r.sort() 498 # return only event times of not cancelled event notices 499 r1 = [x[0] for x in r if not r[3]] 500 tprev=-1 501 ret = [] 502 for t in r1: 503 if t == tprev: 504 #skip time, already in list 505 pass 506 else: 507 ret.append(t) 508 tprev = t 509 return ret
510
511 - def activate(self, obj, process, at = 'undefined', delay = 'undefined', 512 prior = False):
513 """Application function to activate passive process.""" 514 if self._e is None: 515 raise FatalSimerror\ 516 ('Fatal error: simulation is not initialized (call initialize() first)') 517 if not (type(process) == types.GeneratorType): 518 raise FatalSimerror('Activating function which'+ 519 ' is not a generator (contains no \'yield\')') 520 ## obj.sim=self 521 if not obj._terminated and not obj._nextTime: 522 #store generator reference in object; needed for reactivation 523 obj._nextpoint = process 524 if at == 'undefined': 525 at = self._t 526 if delay == 'undefined': 527 zeit = max(self._t, at) 528 else: 529 zeit = max(self._t, self._t + delay) 530 self._e._post(obj, at = zeit, prior = prior)
531
532 - def reactivate(self, obj, at = 'undefined', delay = 'undefined', 533 prior = False):
534 """Application function to reactivate a process which is active, 535 suspended or passive.""" 536 # Object may be active, suspended or passive 537 if not obj._terminated: 538 a = Process('SimPysystem',sim=self) 539 a.cancel(obj) 540 # object now passive 541 if at == 'undefined': 542 at = self._t 543 if delay == 'undefined': 544 zeit = max(self._t, at) 545 else: 546 zeit = max(self._t, self._t + delay) 547 self._e._post(obj, at = zeit, prior = prior)
548
549 - def startCollection(self, when = 0.0, monitors = None, tallies = None):
550 """Starts data collection of all designated Monitor and Tally objects 551 (default = all) at time 'when'. 552 """ 553 class Starter(Process): 554 def collect(self, monitors, tallies): 555 for m in monitors: 556 print m.name 557 m.reset() 558 for t in tallies: 559 t.reset() 560 yield hold, self
561 if monitors is None: 562 monitors = self.allMonitors 563 if tallies is None: 564 tallies = self.allTallies 565 s = Starter() 566 self.activate(s, s.collect(monitors = monitors, tallies = tallies),at = when) 567 568 ## begin waituntil functionality
569 - def _test(self):
570 """ 571 Gets called by simulate after every event, as long as there are processes 572 waiting in self.condQ for a condition to be satisfied. 573 Tests the conditions for all waiting processes. Where condition satisfied, 574 reactivates that process immediately and removes it from queue. 575 """ 576 rList = [] 577 for el in self.condQ: 578 if el.cond(): 579 rList.append(el) 580 self.reactivate(el) 581 for i in rList: 582 self.condQ.remove(i) 583 584 if not self.condQ: 585 self._stopWUStepping()
586
587 - def _waitUntilFunc(self, proc, cond):
588 """ 589 Puts a process 'proc' waiting for a condition into a waiting queue. 590 'cond' is a predicate function which returns True if the condition is 591 satisfied. 592 """ 593 if not cond(): 594 self.condQ.append(proc) 595 proc.cond = cond 596 self._startWUStepping() #signal 'simulate' that a process is waiting 597 # passivate calling process 598 proc._nextTime = None 599 else: 600 #schedule continuation of calling process 601 self._e._post(proc, at = self._t, prior = 1)
602 603 604 ##end waituntil functionality 605
606 - def simulate(self, until = 0):
607 """Schedules Processes / semi - coroutines until time 'until'""" 608 609 """Gets called once. Afterwards, co - routines (generators) return by 610 'yield' with a cargo: 611 yield hold, self, <delay>: schedules the 'self' process for activation 612 after < delay > time units.If <,delay > missing, 613 same as 'yield hold, self, 0' 614 615 yield passivate, self : makes the 'self' process wait to be re - activated 616 617 yield request, self,<Resource > [,<priority>]: request 1 unit from < Resource> 618 with < priority > pos integer (default = 0) 619 620 yield release, self,<Resource> : release 1 unit to < Resource> 621 622 yield waitevent, self,<SimEvent>|[<Evt1>,<Evt2>,<Evt3), . . . ]: 623 wait for one or more of several events 624 625 626 yield queueevent, self,<SimEvent>|[<Evt1>,<Evt2>,<Evt3), . . . ]: 627 queue for one or more of several events 628 629 yield waituntil, self, cond : wait for arbitrary condition 630 631 yield get, self,<buffer > [,<WhatToGet > [,<priority>]] 632 get < WhatToGet > items from buffer (default = 1); 633 <WhatToGet > can be a pos integer or a filter function 634 (Store only) 635 636 yield put, self,<buffer > [,<WhatToPut > [,priority]] 637 put < WhatToPut > items into buffer (default = 1); 638 <WhatToPut > can be a pos integer (Level) or a list of objects 639 (Store) 640 641 EXTENSIONS: 642 Request with timeout reneging: 643 yield (request, self,<Resource>),(hold, self,<patience>) : 644 requests 1 unit from < Resource>. If unit not acquired in time period 645 <patience>, self leaves waitQ (reneges). 646 647 Request with event - based reneging: 648 yield (request, self,<Resource>),(waitevent, self,<eventlist>): 649 requests 1 unit from < Resource>. If one of the events in < eventlist > occurs before unit 650 acquired, self leaves waitQ (reneges). 651 652 Get with timeout reneging (for Store and Level): 653 yield (get, self,<buffer>,nrToGet etc.),(hold, self,<patience>) 654 requests < nrToGet > items / units from < buffer>. If not acquired < nrToGet > in time period 655 <patience>, self leaves < buffer>.getQ (reneges). 656 657 Get with event - based reneging (for Store and Level): 658 yield (get, self,<buffer>,nrToGet etc.),(waitevent, self,<eventlist>) 659 requests < nrToGet > items / units from < buffer>. If not acquired < nrToGet > before one of 660 the events in < eventlist > occurs, self leaves < buffer>.getQ (reneges). 661 662 663 664 Event notices get posted in event - list by scheduler after 'yield' or by 665 'activate' / 'reactivate' functions. 666 667 """ 668 self._stop = False 669 670 if self._e is None: 671 raise FatalSimerror('Simulation not initialized') 672 if self._e._isEmpty(): 673 self._e = None 674 message="SimPy: No activities scheduled" 675 return message 676 677 self._endtime = until 678 message = 'SimPy: Normal exit' 679 dispatch={hold:holdfunc, request:requestfunc, release:releasefunc, 680 passivate:passivatefunc, waitevent:waitevfunc, queueevent:queueevfunc, 681 waituntil:waituntilfunc, get:getfunc, put:putfunc} 682 commandcodes = dispatch.keys() 683 commandwords={hold:'hold', request:'request', release:'release', passivate:'passivate', 684 waitevent:'waitevent', queueevent:'queueevent', waituntil:'waituntil', 685 get:'get', put:'put'} 686 nextev = self._e._nextev ## just a timesaver 687 while not self._stop and self._t <= self._endtime: 688 try: 689 a = nextev() 690 if not a[0] is None: 691 ## 'a' is tuple '(<yield command>, <action>)' 692 if type(a[0][0]) == tuple: 693 ##allowing for yield (request, self, res),(waituntil, self, cond) 694 command = a[0][0][0] 695 else: 696 command = a[0][0] 697 if __debug__: 698 if not command in commandcodes: 699 raise FatalSimerror('Illegal command: yield %s'%command) 700 dispatch[command](a) 701 except FatalSimerror, error: 702 print 'SimPy: ' + error.value 703 sys.exit(1) 704 except Simerror, error: 705 message = 'SimPy: ' + error.value 706 self._stop = True 707 if self._wustep: 708 self._test() 709 self._stopWUStepping() 710 self._e = None 711 return message
712
713 -def scheduler(till = 0):
714 """Schedules Processes / semi - coroutines until time 'till'. 715 Deprecated since version 0.5. 716 """ 717 simulate(until = till)
718
719 -def holdfunc(a):
720 a[0][1]._hold(a)
721
722 -def requestfunc(a):
723 """Handles 'yield request, self, res' and 'yield (request, self, res),(<code>,self, par)'. 724 <code > can be 'hold' or 'waitevent'. 725 """ 726 if type(a[0][0]) == tuple: 727 ## Compound yield request statement 728 ## first tuple in ((request, self, res),(xx, self, yy)) 729 b = a[0][0] 730 ## b[2] == res (the resource requested) 731 ##process the first part of the compound yield statement 732 ##a[1] is the Process instance 733 b[2]._request(arg = (b, a[1])) 734 ##deal with add - on condition to command 735 ##Trigger processes for reneging 736 class _Holder(Process): 737 """Provides timeout process""" 738 def __init__(self,name,sim=None): 739 Process.__init__(self,name=name,sim=sim)
740 def trigger(self, delay): 741 yield hold, self, delay 742 if not proc in b[2].activeQ: 743 proc.sim.reactivate(proc) 744 745 class _EventWait(Process): 746 """Provides event waiting process""" 747 def __init__(self,name,sim=None): 748 Process.__init__(self,name=name,sim=sim) 749 def trigger(self, event): 750 yield waitevent, self, event 751 if not proc in b[2].activeQ: 752 proc.eventsFired = self.eventsFired 753 proc.sim.reactivate(proc) 754 755 #activate it 756 proc = a[0][0][1] # the process to be woken up 757 actCode = a[0][1][0] 758 if actCode == hold: 759 proc._holder = _Holder(name = 'RENEGE - hold for %s'%proc.name, 760 sim=proc.sim) 761 ## the timeout delay 762 proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 763 elif actCode == waituntil: 764 raise FatalSimerror('Illegal code for reneging: waituntil') 765 elif actCode == waitevent: 766 proc._holder = _EventWait(name = 'RENEGE - waitevent for %s'\ 767 %proc.name,sim=proc.sim) 768 ## the event 769 proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 770 elif actCode == queueevent: 771 raise FatalSimerror('Illegal code for reneging: queueevent') 772 else: 773 raise FatalSimerror('Illegal code for reneging %s'%actCode) 774 else: 775 ## Simple yield request command 776 a[0][2]._request(a) 777
778 -def releasefunc(a):
779 a[0][2]._release(a)
780
781 -def passivatefunc(a):
782 a[0][1]._passivate(a)
783
784 -def waitevfunc(a):
785 #if waiting for one event only (not a tuple or list) 786 evtpar = a[0][2] 787 if isinstance(evtpar, SimEvent): 788 a[0][2]._wait(a) 789 # else, if waiting for an OR of events (list / tuple): 790 else: #it should be a list / tuple of events 791 # call _waitOR for first event 792 evtpar[0]._waitOR(a)
793
794 -def queueevfunc(a):
795 #if queueing for one event only (not a tuple or list) 796 evtpar = a[0][2] 797 if isinstance(evtpar, SimEvent): 798 a[0][2]._queue(a) 799 #else, if queueing for an OR of events (list / tuple): 800 else: #it should be a list / tuple of events 801 # call _queueOR for first event 802 evtpar[0]._queueOR(a)
803
804 -def waituntilfunc(par):
805 par[0][1].sim._waitUntilFunc(par[0][1], par[0][2])
806
807 -def getfunc(a):
808 """Handles 'yield get, self, buffer, what, priority' and 809 'yield (get, self, buffer, what, priority),(<code>,self, par)'. 810 <code > can be 'hold' or 'waitevent'. 811 """ 812 if type(a[0][0]) == tuple: 813 ## Compound yield request statement 814 ## first tuple in ((request, self, res),(xx, self, yy)) 815 b = a[0][0] 816 ## b[2] == res (the resource requested) 817 ##process the first part of the compound yield statement 818 ##a[1] is the Process instance 819 b[2]._get(arg = (b, a[1])) 820 ##deal with add - on condition to command 821 ##Trigger processes for reneging 822 class _Holder(Process): 823 """Provides timeout process""" 824 def __init__(self,**par): 825 Process.__init__(self,**par)
826 def trigger(self, delay): 827 yield hold, self, delay 828 #if not proc in b[2].activeQ: 829 if proc in b[2].getQ: 830 a[1].sim.reactivate(proc) 831 832 class _EventWait(Process): 833 """Provides event waiting process""" 834 def __init__(self,**par): 835 Process.__init__(self,**par) 836 def trigger(self, event): 837 yield waitevent, self, event 838 if proc in b[2].getQ: 839 a[1].eventsFired = self.eventsFired 840 a[1].sim.reactivate(proc) 841 842 #activate it 843 proc = a[0][0][1] # the process to be woken up 844 actCode = a[0][1][0] 845 if actCode == hold: 846 proc._holder = _Holder(name='RENEGE - hold for %s'%proc.name, 847 sim=proc.sim) 848 ## the timeout delay 849 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 850 elif actCode == waituntil: 851 raise FatalSimerror('Illegal code for reneging: waituntil') 852 elif actCode == waitevent: 853 proc._holder = _EventWait(name="RENEGE - waitevent for%s"\ 854 %proc.name,sim=proc.sim) 855 ## the event 856 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 857 elif actCode == queueevent: 858 raise FatalSimerror('Illegal code for reneging: queueevent') 859 else: 860 raise FatalSimerror('Illegal code for reneging %s'%actCode) 861 else: 862 ## Simple yield request command 863 a[0][2]._get(a) 864 865
866 -def putfunc(a):
867 """Handles 'yield put' (simple and compound hold / waitevent) 868 """ 869 if type(a[0][0]) == tuple: 870 ## Compound yield request statement 871 ## first tuple in ((request, self, res),(xx, self, yy)) 872 b = a[0][0] 873 ## b[2] == res (the resource requested) 874 ##process the first part of the compound yield statement 875 ##a[1] is the Process instance 876 b[2]._put(arg = (b, a[1])) 877 ##deal with add - on condition to command 878 ##Trigger processes for reneging 879 class _Holder(Process): 880 """Provides timeout process""" 881 def __init__(self,**par): 882 Process.__init__(self,**par)
883 def trigger(self, delay): 884 yield hold, self, delay 885 #if not proc in b[2].activeQ: 886 if proc in b[2].putQ: 887 a[1].sim.reactivate(proc) 888 889 class _EventWait(Process): 890 """Provides event waiting process""" 891 def __init__(self,**par): 892 Process.__init__(self,**par) 893 def trigger(self, event): 894 yield waitevent, self, event 895 if proc in b[2].putQ: 896 a[1].eventsFired = self.eventsFired 897 a[1].sim.reactivate(proc) 898 899 #activate it 900 proc = a[0][0][1] # the process to be woken up 901 actCode = a[0][1][0] 902 if actCode == hold: 903 proc._holder = _Holder(name='RENEGE - hold for %s'%proc.name, 904 sim=proc.sim) 905 ## the timeout delay 906 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 907 elif actCode == waituntil: 908 raise FatalSimerror('Illegal code for reneging: waituntil') 909 elif actCode == waitevent: 910 proc._holder = _EventWait(name='RENEGE - waitevent for %s'\ 911 %proc.name,sim=proc.sim) 912 ## the event 913 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 914 elif actCode == queueevent: 915 raise FatalSimerror('Illegal code for reneging: queueevent') 916 else: 917 raise FatalSimerror('Illegal code for reneging %s'%actCode) 918 else: 919 ## Simple yield request command 920 a[0][2]._put(a) 921 922 # For backward compatibility 923 Globals.sim = Simulation() 924 # End backward compatibility 925 926 if __name__ == '__main__': 927 print 'SimPy.Simulation %s' %__version__ 928 ############# Test / demo functions #############
929 - def test_demo():
930 class Aa(Process): 931 sequIn = [] 932 sequOut = [] 933 def __init__(self, holdtime, name,sim=None): 934 Process.__init__(self, name,sim=sim) 935 self.holdtime = holdtime
936 937 def life(self, priority): 938 for i in range(1): 939 Aa.sequIn.append(self.name) 940 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 941 len(rrr.activeQ) 942 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ] 943 print 'activeQ: ',[(k.name, k._priority[rrr]) \ 944 for k in rrr.activeQ] 945 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 946 'Inconsistent resource unit numbers' 947 print self.sim.now(),self.name, 'requests 1 ', rrr.unitName 948 yield request, self, rrr, priority 949 print self.sim.now(),self.name, 'has 1 ', rrr.unitName 950 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 951 len(rrr.activeQ) 952 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 953 len(rrr.activeQ) 954 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 955 'Inconsistent resource unit numbers' 956 yield hold, self, self.holdtime 957 print self.sim.now(),self.name, 'gives up 1', rrr.unitName 958 yield release, self, rrr 959 Aa.sequOut.append(self.name) 960 print self.sim.now(),self.name, 'has released 1 ', rrr.unitName 961 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ] 962 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 963 len(rrr.activeQ) 964 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 965 'Inconsistent resource unit numbers' 966 967 class Observer(Process): 968 def __init__(self,**vars): 969 Process.__init__(self,**vars) 970 971 def observe(self, step, processes, res): 972 while self.sim.now() < 11: 973 for i in processes: 974 print '++ %s process: %s: active:%s, passive:%s, terminated: %s, interrupted:%s, queuing:%s'\ 975 %(self.sim.now(),i.name, i.active(),i.passive(),\ 976 i.terminated(),i.interrupted(),i.queuing(res)) 977 print 978 yield hold, self, step 979 980 print'\n+++test_demo output' 981 print '****First case == priority queue, resource service not preemptable' 982 s=Simulation() 983 s.initialize() 984 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ, 985 preemptable = 0,sim=s) 986 procs = [] 987 for i in range(10): 988 z = Aa(holdtime = i, name = 'Car ' + str(i),sim=s) 989 procs.append(z) 990 s.activate(z, z.life(priority = i)) 991 o = Observer(sim=s) 992 s.activate(o, o.observe(1, procs, rrr)) 993 a = s.simulate(until = 10000) 994 print a 995 print 'Input sequence: ', Aa.sequIn 996 print 'Output sequence: ', Aa.sequOut 997 998 print '\n****Second case == priority queue, resource service preemptable' 999 s=Simulation() 1000 s.initialize() 1001 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ, 1002 preemptable = 1,sim=s) 1003 procs = [] 1004 for i in range(10): 1005 z = Aa(holdtime = i, name = 'Car ' + str(i),sim=s) 1006 procs.append(z) 1007 s.activate(z, z.life(priority = i)) 1008 o = Observer(sim=s) 1009 s.activate(o, o.observe(1, procs, rrr)) 1010 Aa.sequIn = [] 1011 Aa.sequOut = [] 1012 a = s.simulate(until = 10000) 1013 print a 1014 print 'Input sequence: ', Aa.sequIn 1015 print 'Output sequence: ', Aa.sequOut 1016
1017 - def test_interrupt():
1018 class Bus(Process): 1019 def __init__(self, **vars): 1020 Process.__init__(self, **vars)
1021 1022 def operate(self, repairduration = 0): 1023 print self.sim.now(),'>> %s starts' % (self.name) 1024 tripleft = 1000 1025 while tripleft > 0: 1026 yield hold, self, tripleft 1027 if self.interrupted(): 1028 print 'interrupted by %s' %self.interruptCause.name 1029 print '%s: %s breaks down ' %(now(),self.name) 1030 tripleft = self.interruptLeft 1031 self.interruptReset() 1032 print 'tripleft ', tripleft 1033 s.reactivate(br, delay = repairduration) # breakdowns only during operation 1034 yield hold, self, repairduration 1035 print self.sim.now(),' repaired' 1036 else: 1037 break # no breakdown, ergo bus arrived 1038 print self.sim.now(),'<< %s done' % (self.name) 1039 1040 class Breakdown(Process): 1041 def __init__(self, myBus,sim=None): 1042 Process.__init__(self, name = 'Breakdown ' + myBus.name,sim=sim) 1043 self.bus = myBus 1044 1045 def breakBus(self, interval): 1046 1047 while True: 1048 yield hold, self, interval 1049 if self.bus.terminated(): break 1050 self.interrupt(self.bus) 1051 1052 print'\n\n+++test_interrupt' 1053 s=Simulation() 1054 s.initialize() 1055 b = Bus(name='Bus 1',sim=s) 1056 s.activate(b, b.operate(repairduration = 20)) 1057 br = Breakdown(b,sim=s) 1058 s.activate(br, br.breakBus(200)) 1059 print s.simulate(until = 4000) 1060
1061 - def testSimEvents():
1062 class Waiter(Process): 1063 def __init__(self,**vars): 1064 Process.__init__(self,**vars)
1065 def waiting(self, theSignal): 1066 while True: 1067 yield waitevent, self, theSignal 1068 print '%s: process \'%s\' continued after waiting for %s' %\ 1069 (self.sim.now(),self.name, theSignal.name) 1070 yield queueevent, self, theSignal 1071 print '%s: process \'%s\' continued after queueing for %s' % (now(),self.name, theSignal.name) 1072 1073 class ORWaiter(Process): 1074 def __init__(self,**vars): 1075 Process.__init__(self,**vars) 1076 def waiting(self, signals): 1077 while True: 1078 yield waitevent, self, signals 1079 print self.sim.now(),'one of %s signals occurred' %\ 1080 [x.name for x in signals] 1081 print '\t%s (fired / param)'%\ 1082 [(x.name, x.signalparam) for x in self.eventsFired] 1083 yield hold, self, 1 1084 1085 class Caller(Process): 1086 def __init__(self,**vars): 1087 Process.__init__(self,**vars) 1088 def calling(self): 1089 while True: 1090 signal1.signal('wake up!') 1091 print '%s: signal 1 has occurred'%now() 1092 yield hold, self, 10 1093 signal2.signal('and again') 1094 signal2.signal('sig 2 again') 1095 print '%s: signal1, signal2 have occurred'%now() 1096 yield hold, self, 10 1097 print'\n+++testSimEvents output' 1098 s=Simulation() 1099 s.initialize() 1100 signal1 = SimEvent('signal 1',sim=s) 1101 signal2 = SimEvent('signal 2',sim=s) 1102 signal1.signal('startup1') 1103 signal2.signal('startup2') 1104 w1 = Waiter(name='waiting for signal 1',sim=s) 1105 s.activate(w1, w1.waiting(signal1)) 1106 w2 = Waiter(name='waiting for signal 2',sim=s) 1107 s.activate(w2, w2.waiting(signal2)) 1108 w3 = Waiter(name='also waiting for signal 2',sim=s) 1109 s.activate(w3, w3.waiting(signal2)) 1110 w4 = ORWaiter(name='waiting for either signal 1 or signal 2',sim=s) 1111 s.activate(w4, w4.waiting([signal1, signal2]),prior = True) 1112 c = Caller(name='Caller',sim=s) 1113 s.activate(c, c.calling()) 1114 print s.simulate(until = 100) 1115
1116 - def testwaituntil():
1117 """ 1118 Demo of waitUntil capability. 1119 1120 Scenario: 1121 Three workers require sets of tools to do their jobs. Tools are shared, 1122 scarce resources for which they compete. 1123 """ 1124 class Worker(Process): 1125 def __init__(self, name, heNeeds = [],sim=None): 1126 Process.__init__(self, name,sim=sim) 1127 self.heNeeds = heNeeds
1128 def work(self): 1129 def workerNeeds(): 1130 for item in self.heNeeds: 1131 if item.n == 0: 1132 return False 1133 return True 1134 1135 while self.sim.now() < 8 * 60: 1136 yield waituntil, self, workerNeeds 1137 for item in self.heNeeds: 1138 yield request, self, item 1139 print '%s %s has %s and starts job' % (self.sim.now(),self.name, 1140 [x.name for x in self.heNeeds]) 1141 yield hold, self, random.uniform(10, 30) 1142 for item in self.heNeeds: 1143 yield release, self, item 1144 yield hold, self, 2 #rest 1145 1146 print '\n+++ nwaituntil demo output' 1147 random.seed(12345) 1148 s=Simulation() 1149 s.initialize() 1150 brush = Resource(capacity = 1, name = 'brush',sim=s) 1151 ladder = Resource(capacity = 2, name = 'ladder',sim=s) 1152 hammer = Resource(capacity = 1, name = 'hammer',sim=s) 1153 saw = Resource(capacity = 1, name = 'saw',sim=s) 1154 painter = Worker('painter',[brush, ladder],sim=s) 1155 s.activate(painter, painter.work()) 1156 roofer = Worker('roofer',[hammer, ladder, ladder],sim=s) 1157 s.activate(roofer, roofer.work()) 1158 treeguy = Worker('treeguy',[saw, ladder],sim=s) 1159 s.activate(treeguy, treeguy.work()) 1160 for who in (painter, roofer, treeguy): 1161 print '%s needs %s for his job' %\ 1162 (who.name,[x.name for x in who.heNeeds]) 1163 print 1164 print s.simulate(until = 9 * 60) 1165 1166 ## ------------------------------------------------------------- 1167 ## TEST COMPOUND 'YIELD REQUEST' COMMANDS 1168 ## ------------------------------------------------------------- 1169 1170 ## ------------------------------------------------------------- 1171 ## TEST 'yield (request, self, res),(hold, self, delay)' 1172 ## == timeout renege 1173 ## ------------------------------------------------------------- 1174
1175 - class JobTO(Process):
1176 """ Job class for testing timeout reneging 1177 """
1178 - def __init__(self, server = None, name = '',sim=None):
1179 Process.__init__(self, name,sim=sim) 1180 self.res = server 1181 self.gotResource = None
1182
1183 - def execute(self, timeout, usetime):
1184 yield (request, self, self.res),(hold, self, timeout) 1185 if self.acquired(self.res): 1186 self.gotResource = True 1187 yield hold, self, usetime 1188 yield release, self, self.res 1189 else: 1190 self.gotResource = False
1191 1192
1193 - def testNoTimeout():
1194 """Test that resource gets acquired without timeout 1195 """ 1196 s=Simulation() 1197 s.initialize() 1198 res = Resource(name = 'Server', capacity = 1,sim=s) 1199 usetime = 5 1200 timeout = 1000000 1201 j1 = JobTO(server = res, name = 'Job_1',sim=s) 1202 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime)) 1203 j2 = JobTO(server = res, name = 'Job_2',sim=s) 1204 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime)) 1205 s.simulate(until = 2 * usetime) 1206 assert s.now() == 2 * usetime, 'time not == 2 * usetime' 1207 assert j1.gotResource and j2.gotResource,\ 1208 'at least one job failed to get resource' 1209 assert not (res.waitQ or res.activeQ),\ 1210 'job waiting or using resource'
1211
1212 - def testTimeout1():
1213 """Test that timeout occurs when resource busy 1214 """ 1215 s=Simulation() 1216 s.initialize() 1217 res = Resource(name = 'Server', capacity = 1, monitored = True,sim=s) 1218 usetime = 5 1219 timeout = 3 1220 j1 = JobTO(server = res, name = 'Job_1',sim=s) 1221 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime)) 1222 j2 = JobTO(server = res, name = 'Job_2',sim=s) 1223 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime)) 1224 s.simulate(until = 2 * usetime) 1225 assert(s.now() == usetime),'time not == usetime' 1226 assert(j1.gotResource),'Job_1 did not get resource' 1227 assert(not j2.gotResource),'Job_2 did not renege' 1228 assert not (res.waitQ or res.activeQ),\ 1229 'job waiting or using resource'
1230
1231 - def testTimeout2():
1232 """Test that timeout occurs when resource has no capacity free 1233 """ 1234 s=Simulation() 1235 s.initialize() 1236 res = Resource(name = 'Server', capacity = 0,sim=s) 1237 usetime = 5 1238 timeout = 3 1239 j1 = JobTO(server = res, name = 'Job_1',sim=s) 1240 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime)) 1241 j2 = JobTO(server = res, name = 'Job_2',sim=s) 1242 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime)) 1243 s.simulate(until = 2 * usetime) 1244 assert s.now() == timeout, 'time %s not == timeout'%now() 1245 assert not j1.gotResource, 'Job_1 got resource' 1246 assert not j2.gotResource, 'Job_2 got resource' 1247 assert not (res.waitQ or res.activeQ),\ 1248 'job waiting or using resource'
1249 1250 ## ------------------------------------------------------------------ 1251 ## TEST 'yield (request, self, res),(waitevent, self, event)' 1252 ## == event renege 1253 ## ------------------------------------------------------------------
1254 - class JobEvt(Process):
1255 """ Job class for testing event reneging 1256 """
1257 - def __init__(self, server = None, name = '',sim=None):
1258 Process.__init__(self, name,sim=sim) 1259 self.res = server 1260 self.gotResource = None
1261
1262 - def execute(self, event, usetime):
1263 yield (request, self, self.res),(waitevent, self, event) 1264 if self.acquired(self.res): 1265 self.gotResource = True 1266 yield hold, self, usetime 1267 yield release, self, self.res 1268 else: 1269 self.gotResource = False
1270
1271 - class JobEvtMulti(Process):
1272 """ Job class for testing event reneging with multi - event lists 1273 """
1274 - def __init__(self, server = None, name = '',sim=None):
1275 Process.__init__(self, name,sim=sim) 1276 self.res = server 1277 self.gotResource = None
1278
1279 - def execute(self, eventlist, usetime):
1280 yield (request, self, self.res),(waitevent, self, eventlist) 1281 if self.acquired(self.res): 1282 self.gotResource = True 1283 yield hold, self, usetime 1284 yield release, self, self.res 1285 else: 1286 self.gotResource = False
1287
1288 - class FireEvent(Process):
1289 """Fires reneging event 1290 """
1291 - def __init__(self,**vars):
1292 Process.__init__(self,**vars)
1293 - def fire(self, fireDelay, event):
1294 yield hold, self, fireDelay 1295 event.signal()
1296
1297 - def testNoEvent():
1298 """Test that processes acquire resource normally if no event fires 1299 """ 1300 s=Simulation() 1301 s.initialize() 1302 res = Resource(name = 'Server', capacity = 1,sim=s) 1303 event = SimEvent(name='Renege_trigger',sim=s) #never gets fired 1304 usetime = 5 1305 j1 = JobEvt(server = res, name = 'Job_1',sim=s) 1306 s.activate(j1, j1.execute(event = event, usetime = usetime)) 1307 j2 = JobEvt(server = res, name = 'Job_2',sim=s) 1308 s.activate(j2, j2.execute(event = event, usetime = usetime)) 1309 s.simulate(until = 2 * usetime) 1310 # Both jobs should get server (in sequence) 1311 assert s.now() == 2 * usetime, 'time not == 2 * usetime' 1312 assert j1.gotResource and j2.gotResource,\ 1313 'at least one job failed to get resource' 1314 assert not (res.waitQ or res.activeQ),\ 1315 'job waiting or using resource'
1316
1317 - def testWaitEvent1():
1318 """Test that signalled event leads to renege when resource busy 1319 """ 1320 s=Simulation() 1321 s.initialize() 1322 res = Resource(name = 'Server', capacity = 1,sim=s) 1323 event = SimEvent('Renege_trigger',sim=s) 1324 usetime = 5 1325 eventtime = 1 1326 j1 = JobEvt(server = res, name = 'Job_1',sim=s) 1327 s.activate(j1, j1.execute(event = event, usetime = usetime)) 1328 j2 = JobEvt(server = res, name = 'Job_2',sim=s) 1329 s.activate(j2, j2.execute(event = event, usetime = usetime)) 1330 f = FireEvent(name = 'FireEvent',sim=s) 1331 s.activate(f, f.fire(fireDelay = eventtime, event = event)) 1332 s.simulate(until = 2 * usetime) 1333 # Job_1 should get server, Job_2 renege 1334 assert(s.now() == usetime),'time not == usetime' 1335 assert(j1.gotResource),'Job_1 did not get resource' 1336 assert(not j2.gotResource),'Job_2 did not renege' 1337 assert not (res.waitQ or res.activeQ),\ 1338 'job waiting or using resource'
1339
1340 - def testWaitEvent2():
1341 """Test that renege - triggering event can be one of an event list 1342 """ 1343 s=Simulation() 1344 s.initialize() 1345 res = Resource(name = 'Server', capacity = 1,sim=s) 1346 event1 = SimEvent('Renege_trigger_1',sim=s) 1347 event2 = SimEvent('Renege_trigger_2',sim=s) 1348 usetime = 5 1349 eventtime = 1 #for both events 1350 j1 = JobEvtMulti(server = res, name = 'Job_1',sim=s) 1351 s.activate(j1, j1.execute(eventlist = [event1, event2],usetime = usetime)) 1352 j2 = JobEvtMulti(server = res, name = 'Job_2',sim=s) 1353 s.activate(j2, j2.execute(eventlist = [event1, event2],usetime = usetime)) 1354 f1 = FireEvent(name = 'FireEvent_1',sim=s) 1355 s.activate(f1, f1.fire(fireDelay = eventtime, event = event1)) 1356 f2 = FireEvent(name = 'FireEvent_2',sim=s) 1357 s.activate(f2, f2.fire(fireDelay = eventtime, event = event2)) 1358 s.simulate(until = 2 * usetime) 1359 # Job_1 should get server, Job_2 should renege 1360 assert(s.now() == usetime),'time not == usetime' 1361 assert(j1.gotResource),'Job_1 did not get resource' 1362 assert(not j2.gotResource),'Job_2 did not renege' 1363 assert not (res.waitQ or res.activeQ),\ 1364 'job waiting or using resource'
1365 1366 testNoTimeout() 1367 testTimeout1() 1368 testTimeout2() 1369 testNoEvent() 1370 testWaitEvent1() 1371 testWaitEvent2() 1372 test_demo() 1373 test_interrupt() 1374 testSimEvents() 1375 testwaituntil() 1376