1

If I have a simpy.Process that creates nested processes, is there a way to get a list of all the active/alive processes from it's simpy.Environment?

Basically I've created a tree of simpy processes and at some point I want to interrupt all of the active processes. Having every process listen for simpy.Interrupt then in turn interrupting processes started by it appears to be too tedious and prone to errors from forgetting to add it to the list of child processes to be interrupted.

gameveloster
  • 901
  • 1
  • 6
  • 18
  • [The manual has a section on resource usage and monitoring[(https://simpy.readthedocs.io/en/latest/topical_guides/monitoring.html#resource-usage), have you gone through that and applied the suggestions? Why did it not work for you? – Saaru Lindestøkke Feb 28 '23 at 17:07
  • sending interrupts to every process does not sound very graceful – Michael Mar 01 '23 at 01:07

3 Answers3

0

In this example I interrupt a top process, which propagates the interrupt to its children. Those children then propagates the interrupt to their children. Each process is only responsible for its children. Using exceptions to drive logic is not considered a best practice.

"""
Sim showing how to interupt a nested chain of processes 
using event interrupts

In this example the process that starts a child process,
also interupts the child process when it receives a interrupt exception,
propagating the exception down the nested chain

Note that the top process is the frist process to stop

Programmer: Michael R. Gibbs
"""

import simpy

def bottom_process(env):
    """
    end process in nested chain of processes
    """

    print(f'{env.now} starting bottom process')

    try:
        # do something that take a bit of sim time
        yield env.timeout(10)
        print(f'{env.now} bottom process finished')

    except simpy.Interrupt:
        print(f'{env.now} bottom process interupted')

def mid_process(env):
    """
    Middle process in a nested chain of processes
    """

    print(f'{env.now} starting middle process')

    # start next nested process
    bottom_event = env.process(bottom_process(env))

    try:
        # wait for child to finish
        yield bottom_event
        print(f'{env.now} mid process finished')

    except simpy.Interrupt:
        print(f'{env.now} mid process interupted')

        # also interupt child
        bottom_event.interrupt()

def top_process(env):
    """
    The top starting process in a chain of nested processes
    """

    print(f'{env.now} starting top process')

    # start the next nested process in the chain
    mid_event = env.process(mid_process(env))

    try:
        yield mid_event
        print(f'{env.now} top process finished')

    except simpy.Interrupt:
        print(f'{env.now} top process interupted')

        # also interrupt the nested child process
        mid_event.interrupt()

def main_process(env):
    """
    Main process that starts a nested chain of processes
    and then interupts the top process
    """

    print(f'{env.now} starting main')

    top_event = env.process(top_process(env))

    yield env.timeout(3)

    top_event.interrupt()

    print(f'{env.now} main has finish')

# boot up the whole mess
env = simpy.Environment()

env.process(main_process(env))

env.run(100)

print('done')
Michael
  • 1,671
  • 2
  • 4
  • 8
0

In this example I create a bomb event that explodes with a interrupt exception. This event is passed down the nested chain where the bottom process catches the interrupt exception, and then re-throw the exception back up the nested chain. Again, using exceptions to drive logic is not a best practice.

"""
Sim showing how to interupt a nested chain of processes 
using a bomb event that throws a interrupt exception to stop processing

In this example, one bomb event is created and passed down to the bottom
process.  When the bomb 'explodes' the bottom process catches the interupt
exception and re-throws the interrput exception up the chain

Note that the bottom process is the first to be interupted

Programmer: Michael R. Gibbs
"""

import simpy

def bottom_process(env, bomb_event):
    """
    end process in nested chain of processes
    """

    print(f'{env.now} starting bottom process')

    try:
        # do something that take a bit of sim time
        # also yield to the bomb to catch its interrupt
        yield env.any_of([env.timeout(10), bomb_event])

        print(f'{env.now} bottom process finished')

    except simpy.Interrupt as err:
        print(f'{env.now} bottom process interupted')

        # re-raise interupt up the chain
        raise err

def mid_process(env, bomb_event):
    """
    Middle process in a nested chain of processes
    """

    print(f'{env.now} starting middle process')

    # start next nested process, pass down the bomb
    bottom_event = env.process(bottom_process(env, bomb_event))

    try:
        # wait for child to finish
        yield bottom_event
        print(f'{env.now} mid process finished')

    except simpy.Interrupt as err:
        print(f'{env.now} mid process interupted')

        # re-raise the interupt up the chain
        raise err


def top_process(env, bomb_event):
    """
    The top starting process in a chain of nested processes
    """

    print(f'{env.now} starting top process')

    # start the next nested process in the chain
    # pass down the bomb
    mid_event = env.process(mid_process(env, bomb_event))

    try:
        yield mid_event
        print(f'{env.now} top process finished')

    except simpy.Interrupt:
        print(f'{env.now} top process interupted')


def bomb_process(env):
    """
    Need a process that never ends because
    only processes can be interrupted
    """

    yield env.event() # this event will never be triggered


def main_process(env):
    """
    Main process that starts a nested chain of processes,
    passing down a bomb event
    """

    print(f'{env.now} starting main')

    # create the bomb event from a process
    bomb_event = env.process(bomb_process(env))

    env.process(top_process(env, bomb_event))

    yield env.timeout(3)

    bomb_event.interrupt()

    print(f'{env.now} main has finish')

# boot up the whole mess
env = simpy.Environment()

env.process(main_process(env))

env.run(100)

print('done')
Michael
  • 1,671
  • 2
  • 4
  • 8
0

In this example I use a event to signal when to stop processing. The event is passed down the nested chain to the bottom processes. When the event is triggered, the bottom process stops processing and returns a status flag to the calling process which uses to status to decide how to proceed, also stopping.

"""
Sim showing how to interupt a nested chain of processes 
using a event to signal when to stop processing

This example is perhaps the most gracefull

In this example, event is created and passed down to the bottom
process.  When the event is triggered the bottom process stops
process and returns a event value 'Stopped', so the calling process
will know the child stopped early.  No itterupts are used.

Note that the bottom process is the first to be interupted

Programmer: Michael R. Gibbs
"""

import simpy

def bottom_process(env, stop_event):
    """
    end process in nested chain of processes

    returns a end status of Success, Stopped, Interrupted
    """

    print(f'{env.now} starting bottom process')

    try:
        # do something that takes a bit of sim time
        # also yield to the stop event for early end of processing
        event_map = yield env.any_of([env.timeout(10), stop_event])

        if stop_event in event_map:
            # the stop event fired, do not finish processing
            print(f'{env.now} bottom process has been stopped')
            return 'Stopped'

        print(f'{env.now} bottom process finished')

        return 'Success'


    except simpy.Interrupt as err:
        print(f'{env.now} bottom process interupted')

        return 'Interrupted'


def mid_process(env, stop_event):
    """
    Middle process in a nested chain of processes

    return end status of Succes, Interrupted, or Stopped
    """

    print(f'{env.now} starting middle process')

    # start next nested process, pass down the bomb
    bottom_event = env.process(bottom_process(env, stop_event))

    try:
        # wait for child to finish
        status = yield bottom_event

        # use the status to check if processing was stopped early
        if status == 'Stopped':
            print(f'{env.now} middle process has been stopped')
            return 'Stopped'
        
        print(f'{env.now} mid process finished')
        return 'Success'

    except simpy.Interrupt as err:
        print(f'{env.now} mid process interupted')

        return 'Interrupted'


def top_process(env, stop_event):
    """
    The top starting process in a chain of nested processes

    returns a status of Success, Interrupted, or Stopped
    """

    print(f'{env.now} starting top process')

    # start the next nested process in the chain
    # pass down the bomb
    mid_event = env.process(mid_process(env, stop_event))

    try:
        status = yield mid_event

        # check the status to see if processing was stopped early
        if status == 'Stopped':
            print(f'{env.now} Top process has been stopped')
            return 'Stopped'
        
        print(f'{env.now} top process finished')
        return 'Success'

    except simpy.Interrupt:
        print(f'{env.now} top process interupted')
        return 'Interrupted'

def main_process(env):
    """
    Main process that starts a nested chain of processes,
    passing down a stop event
    """

    print(f'{env.now} starting main')

    # create a event to signal when to stop
    stop_event = env.event()

    env.process(top_process(env, stop_event))

    yield env.timeout(3)

    # trigger event to stop processing
    stop_event.succeed()

    print(f'{env.now} main has finish')

# boot up the whole mess
env = simpy.Environment()

env.process(main_process(env))

env.run(100)

print('done')
Michael
  • 1,671
  • 2
  • 4
  • 8