Search code examples
pythonsimpy

Simpy event ordering


I'm confused about Simpy events and the ordering in which things happen. In the code below, the sender process generates an event every tick and gives it a unique sequence number. The receiver process is waiting on this event and prints the sequence number when received. The output of the code is:

Sender sent req at time 0 with num 0
Receiver Received req at time 0 with num 0
Sender sent req at time 1 with num 1
Receiver Received req at time 1 with num 1
Sender sent req at time 2 with num 2
Receiver Received req at time 2 with num 2
Sender sent req at time 3 with num 3
Receiver Received req at time 3 with num 3
Sender sent req at time 4 with num 4
Receiver Received req at time 4 with num 4

In other words, the receiver is woken up only once for every event that the sender sends. However, I'm not sure if this is entirely safe and will consistently work like this. For example, is the following sequence possible where the receiver could see the same event twice?

  1. The sender generates an event at t0.
  2. The receiver receives the event at t0.
  3. The sender and receiver sleep for 1 tick.
  4. The receiver is the first process to wake up. It executes its yield statement and finds that the event generated at t0 is still triggered.
  5. The sender wakes up, clears the event set at t0 and generates a new event.

Please can you help me understand exactly how this ordering works.

Thanks.

import simpy

class Test:

    def __init__(self, env):
        self.env = env
        self.req = env.event()
        self.ack = env.event()

        self.env.process(self.sender())
        self.env.process(self.receiver())

    def sender(self):

        num = 0

        while True:
            self.req.succeed(num)
            print('Sender sent req at time %d with num %s' % (self.env.now, num))

            # Assert req for 1 cycle
            yield self.env.timeout(1)
            self.req = self.env.event()

            num += 1

    def receiver(self):

        while True:

            val = yield self.req
            print('Receiver Received req at time %d with num %s' % (self.env.now, val))
            yield self.env.timeout(1)

env = simpy.Environment()

test = Test(env)

env.run(until=25)

Solution

  • No, you cannot rely on the order events will happen at a tick. In general, if you want to do things in a asynchronous way you need a queue. In my example I use simpy Stores as message queues. Note that this just demos how to do some communicating and may need some changes to sim the protocol you are going for.

    import simpy
    
    class Test:
    
        def __init__(self, env):
            self.env = env
            self.req = simpy.Store(env)
            self.ack = simpy.Store(env)
    
            self.env.process(self.sender())
            self.env.process(self.receiver())
    
        def sender(self):
    
            num = 0
    
            while True:
    
                
                req = self.env.event()
                self.req.put(req)
    
                print('Sender sent req at time %d with num %s' % (self.env.now, num))
    
                # take some time to send req
                yield self.env.timeout(1)
                req.succeed(num)
    
                # wait for ack
                ack = yield self.ack.get()
                print('Sender got ack at time %d' % (self.env.now))
                val = yield ack
                print('Sender ack triggered at time %d with num %s' % (self.env.now, val))
    
                num += 1
    
        def receiver(self):
    
            while True:
    
                req = yield self.req.get()
                print('Receiver Received req at time %d' % (self.env.now))
                val = yield req
                print('Receiver req triggered at time %d with num %s' % (self.env.now, val))
    
                ack = self.env.event()
                self.ack.put(ack)
                
                # take some time to send ack
                yield self.env.timeout(1)
    
                ack.succeed(val)
                print('Receiver finished sending ack at time %d with num %s' % (self.env.now, val))
    
    
    
    
    env = simpy.Environment()
    
    test = Test(env)
    
    env.run(until=25)