#!/usr/bin/env python

# TimeOutInt.py

# Same as TimeOut.py but using interrupts.  A network node sends a message
# but also sets a timeout period; if the node times out, it assumes the
# message it had sent was lost, and it will send again.  The time to get
# an acknowledgement for a message is exponentially distributed with
# mean 1.0, and the timeout period is 0.5.  Immediately after receiving
# an acknowledgement, the node sends out a new message.

# We find the proportion of messages which timeout.  The output should
# be about 0.61.

from SimPy.Simulation import *
from random import Random,expovariate

class Node(Process):
   def __init__(self):
      Process.__init__(self)  
      self.NMsgs = 0  # number of messages sent
      self.NTimeOuts = 0  # number of timeouts which have occurred
   def Run(self):
      from SimPy.Simulation import _e
      while 1:
         self.NMsgs += 1
         # set up the timeout
         G.TO = TimeOut()
         activate(G.TO,G.TO.Run())
         # wait for ACK, but could be timeout
         yield hold,self,G.Rnd.expovariate(1.0)
         if self.interrupted():
            self.NTimeOuts += 1
         else:  self.cancel(G.TO)

class TimeOut(Process):
   TOPeriod = 0.5
   def __init__(self):
      Process.__init__(self)  
   def Run(self):
      from SimPy.Simulation import _e
      yield hold,self,TimeOut.TOPeriod
      self.interrupt(G.Nd)

class G:  # globals
   Rnd = Random(12345)
   Nd = Node()

def main():
   initialize()  
   activate(G.Nd,G.Nd.Run())
   simulate(until=10000.0)
   print 'the percentage of timeouts was', float(G.Nd.NTimeOuts)/G.Nd.NMsgs

if __name__ == '__main__':  main()
