2

simpy newbie here. I just started to make Speaker-Moderator simulation with simpy. Here is my code

import simpy

def speaker(env):
    try:
        print("Speaker start to talk at: {}".format(env.now))
        speak_time = 40
        print ("Speaker want to speak for {}".format(speak_time))
        yield env.timeout(speak_time)
        print ("Speaker finish the talk at: {}".format(env.now))
   except simpy.Interrupt as interrupt:
        print (interrupt.cause)

def moderator(env):
    for i in range(3):
        print("Moderator let the speaker number {} to begin the speak".format(i))
        speaker_proc = env.process(speaker(env))

        print("Time now: {}".format(env.now))
        time_limit = env.timeout(30)
        results = yield speaker_proc | time_limit
        print("Moderator check whether speaker passed the time limit or no")
        print("Time limit passed: {}".format (speaker_proc not in results))


        if speaker_proc not in results:
            print("Time now: {}".format(env.now))
            speaker_proc.interrupt()
            print ("Moderator stop the talk at: {}".format(env.now))
        print()
        print()

env = simpy.Environment()
env.process(moderator(env))
env.run()    

When I run it with speak_time > 30 or speak_time < 30, It has no problem but if I change speak_time in function speaker become 30 it will get error like this:

RuntimeError: <Process(speaker) object at 0x9e17930> has terminated and cannot be interrupted.

What happened?

yusri
  • 21
  • 4

1 Answers1

1

You hit a strange corner case here. While the behavior you observed is totally intended, it is not very easy to understand for SimBy newbies.

I added two print() to your moderator() process to show what’s going on:

import simpy


def speaker(env):
    try:
        print("Speaker start to talk at: {}".format(env.now))
        speak_time = 30
        print("Speaker want to speak for {}".format(speak_time))
        yield env.timeout(speak_time)
        print("Speaker finish the talk at: {}".format(env.now))
    except simpy.Interrupt as interrupt:
        print(interrupt.cause)


def moderator(env):
    for i in range(3):
        print("Moderator let the speaker number {} to begin the speak".format(i))
        speaker_proc = env.process(speaker(env))

        print("Time now: {}".format(env.now))
        time_limit = env.timeout(30)
        print('Timeout created')
        results = yield speaker_proc | time_limit
        print("Moderator check whether speaker passed the time limit or no")
        print("Time limit passed: {}".format(speaker_proc not in results))

        if speaker_proc not in results:
            print("Time now: {}".format(env.now))
            print(speaker_proc.is_alive)
            speaker_proc.interrupt()
            print("Moderator stop the talk at: {}".format(env.now))
        print()
        print()

env = simpy.Environment()
env.process(moderator(env))
env.run()

The output:

Moderator let the speaker number 0 to begin the speak
Time now: 0
Timeout created
Speaker start to talk at: 0
Speaker want to speak for 30
Speaker finish the talk at: 30
Moderator check whether speaker passed the time limit or no
Time limit passed: True
Time now: 30
False
Traceback (most recent call last):

So what happend?

  1. The moderator is active until its first yield. During that time, it creates the speaker process and the timeout.

  2. The timeout is immediately triggered and added to the event queue.

  3. The moderator yields the conditon event.

  4. The speaker runs and creates its timeout. It is also immediately triggered and scheduled (at the same time as the moderator's timeout, but it is inserted after it in the event queue).

  5. The Condition event is triggered when the moderator's timeout is processed.

  6. When the moderator is resumed, the condition event only holds the result of moderator's timeout, but the speaker processes terminated, too. You can check this via the speaker_proc.is_alive property.

This guide contains some more in-depth information about this topic.

Stefan Scherfke
  • 3,012
  • 1
  • 19
  • 28
  • After read some examples, In moderator process function I change If-clause "if speaker_proc not in results:" become "if not speaker_proc.triggered:" and it became working again. Still, don't know why it can work again. I think I will read the documentation again – yusri Jan 22 '17 at 16:33
  • 1
    The basic problem is, that both timeouts, the moderator's and the speaker's, are scheduled at the same time. But since they have to be serialized, the one from the moderator get's processed first. When the condition event evaluates, the moderator timeout is already processed but the speaker processes is still in the queue. So the condition event does know this. When moderator resumes, the speakers has been processed. – Stefan Scherfke Jan 22 '17 at 18:11
  • @StefanScherfke I have a similar situation, is there a recommended way or design pattern to get out of this corner case ? right now, I'm adding an epsilon value to the timeout so if the delays were to be equal, the process has time to finish properly before the timeout – YeO Dec 19 '21 at 13:20