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