melekus
melekus

Reputation: 3

How to service multiple resources in one process (SimPy simulation)?

I have a problem with simulating simple work conserving scheduler in SimPy. I want this scheduler to have 2 queues to work as simple round robin which services packet from queue number 1 and then services packet from queue number 2. If one of the queues is empty it goes to service packets from other queue (work conserving). Packets that were already serviced are sent to 1 common output.

I've already written a code based on this code (https://www.grotto-networking.com/DiscreteEventPython.html) to create such solution but it doesn't work as i wanted. Packets which are sent to queue number 1 are being serviced but packets in queue number 2 aren't. I think the problem might be with having multiple resources for 1 process and i don't know how to resolve this problem.

class RoundRobinQueue(object):
    def __init__(self, env, rate, qlimit=None, limit_bytes=True):
        self.store = simpy.Store(env)
        self.store2 = simpy.Store(env)
        self.rate = rate
        self.env = env
        self.out = None
        self.packets_rec = 0
        self.packets_drop = 0
        self.qlimit = qlimit
        self.limit_bytes = limit_bytes
        self.byte_size = 0  # Current size of the queue in bytes
        self.busy = 0  # Used to track if a packet is currently being sent
        self.action = env.process(self.run())  # starts the run() method as a SimPy process
        self.trigger = 1

    def run(self):
        while True:
            if (self.trigger == 0 and len(self.store.items)>=0):
                self.trigger = 1
                msg = (yield self.store.get())
                self.byte_size -= msg.size
                self.busy = 1
                yield self.env.timeout(msg.size * 8.0 / self.rate)
                self.out.put(msg)
                self.busy = 0
            else:
                self.trigger = 1

            if (self.trigger == 1 and len(self.store2.items)>=0):
                self.trigger = 0
                msg2 = (yield self.store2.get())
                self.byte_size -= msg2.size
                self.busy = 1
                yield self.env.timeout(msg2.size * 8.0 / self.rate)
                self.out.put(msg2)
                self.busy = 0
            else:
                self.trigger = 0

Upvotes: 0

Views: 533

Answers (2)

Michael
Michael

Reputation: 1914

looks like when both queues are empty it falls into a infinite loop, added a check for that. Also includes the fix where it only pulls from a queue if it is not empty

def run(self):
    while True:
        if len(self.store.items) + len(self.store2.items) ==0:
            # both queue are empty, wait a sec to avoid a infinate loop
            yield self.env.timeout(1)
        else:
            if len(self.store.items)>0:
                self.trigger = 1
                msg = (yield self.store.get())
                self.byte_size -= msg.size
                self.busy = 1
                yield self.env.timeout(msg.size * 8.0 / self.rate)
                self.out.put(msg)
                self.busy = 0
            else:
                self.trigger = 1

            if len(self.store2.items)>0:
                self.trigger = 0
                msg2 = (yield self.store2.get())
                self.byte_size -= msg2.size
                self.busy = 1
                yield self.env.timeout(msg2.size * 8.0 / self.rate)
                self.out.put(msg2)
                self.busy = 0
            else:
                self.trigger = 0

Upvotes: 1

Michael
Michael

Reputation: 1914

i think

len(self.store.items)>=0 

will always be true because a empty queue will have length of 0

to skip when the queue is empty I think

if (self.trigger == 0 and len(self.store.items)>=0):

should be

if len(self.store.items) > 0:

and

if (self.trigger == 1 and len(self.store2.items)>=0):

should be

if len(self.store2.items) > 0:

Upvotes: 1

Related Questions