SimPy Events Explained: How Things Happen in Simulation

Events are the heartbeat of SimPy. Every time something happens—a timeout expires, a resource becomes available, a process completes—that's an event.

What Is an Event?

An event is something that will occur at a specific point in simulation time. Processes yield events to pause and wait for them.

event = env.timeout(5)  # Event that occurs in 5 time units
yield event            # Wait for it

Event Types

SimPy has several event types:

Event What It Does
Timeout Fires after a duration
Process Fires when process completes
Event Manual trigger
AnyOf Fires when any event occurs
AllOf Fires when all events occur

Timeout Events

The most common event. Wait for time to pass:

def process(env):
    print(f"Start: {env.now}")
    yield env.timeout(10)
    print(f"After 10: {env.now}")

Zero-duration timeouts are valid:

yield env.timeout(0)  # Yield but resume immediately

Useful for letting other processes run.

Process Events

A process is itself an event. Wait for another process to complete:

def worker(env):
    yield env.timeout(5)
    return "Done!"

def manager(env):
    result = yield env.process(worker(env))
    print(result)  # "Done!"

You can also store the process and wait later:

proc = env.process(worker(env))
# Do other things...
yield proc

Manual Events

Create events that you trigger yourself:

def waiter(env, event):
    print(f"Waiting at {env.now}")
    yield event
    print(f"Triggered at {env.now}")

def trigger(env, event):
    yield env.timeout(5)
    event.succeed()  # Trigger the event

env = simpy.Environment()
my_event = env.event()
env.process(waiter(env, my_event))
env.process(trigger(env, my_event))
env.run()

Events can also fail:

event.fail(Exception("Something went wrong"))

AnyOf: Wait for Any

Wait until any event in a list occurs:

def process(env):
    e1 = env.timeout(5)
    e2 = env.timeout(10)

    result = yield e1 | e2  # Wait for either
    print(f"First event at {env.now}")  # Time 5

The | operator creates an AnyOf event.

Check which event(s) occurred:

result = yield e1 | e2
if e1 in result:
    print("e1 happened")

AllOf: Wait for All

Wait until all events occur:

def process(env):
    e1 = env.timeout(5)
    e2 = env.timeout(10)

    result = yield e1 & e2  # Wait for both
    print(f"Both done at {env.now}")  # Time 10

The & operator creates an AllOf event.

Conditional Waits

Combine AnyOf with timeouts for deadlines:

def customer(env, resource):
    req = resource.request()
    patience = env.timeout(10)

    result = yield req | patience

    if req in result:
        print("Got the resource!")
        yield env.timeout(5)  # Use it
        resource.release(req)
    else:
        print("Gave up waiting")
        req.cancel()  # Important: cancel unused request

This is how you model customers who leave if the queue is too long.

Event Values

Events can carry values:

event = env.event()
event.succeed(value="Hello!")

def process(env, event):
    result = yield event
    print(result)  # "Hello!"

Timeouts have None as their value by default.

Chaining Events

Events can trigger other events:

def on_complete(event):
    print("Callback triggered!")

event = env.timeout(5)
event.callbacks.append(on_complete)

But usually, you just yield multiple events sequentially.

Creating Custom Events

For advanced use, subclass Event:

class MyEvent(simpy.Event):
    def __init__(self, env, data):
        super().__init__(env)
        self.data = data

Most simulations don't need this. The built-in events handle common cases.

Event States

Events have states:

Check state:

if event.triggered:
    print("Event has occurred")
if event.processed:
    print("Event has been processed")

Common Patterns

Timeout with Cleanup

def process(env, resource):
    req = resource.request()
    timeout = env.timeout(10)

    result = yield req | timeout

    if req not in result:
        req.cancel()  # Clean up!

Multiple Parallel Waits

def process(env):
    events = [env.timeout(random.randint(1, 10)) for _ in range(5)]

    while events:
        result = yield simpy.AnyOf(env, events)
        for event in result:
            events.remove(event)
            print(f"Event completed at {env.now}")

Signalling Between Processes

done_flag = env.event()

def worker(env):
    yield env.timeout(10)
    done_flag.succeed()

def monitor(env):
    yield done_flag
    print("Worker finished!")

Gotchas

Reusing Events

Events can only fire once. Create new ones for repeated use:

# Wrong
event = env.event()
yield event
yield event  # Error!

# Right
event1 = env.event()
yield event1
event2 = env.event()
yield event2

Forgetting to Cancel

When using AnyOf with requests, cancel unused requests:

result = yield req | timeout
if req not in result:
    req.cancel()  # Don't forget!

Summary

Events are: - What processes wait for - Created by timeouts, processes, resources - Combinable with | (any) and & (all) - Triggerable manually with succeed() or fail()

Master events. Master SimPy.

Next Steps


Discover the Power of Simulation

Want to become a go-to expert in simulation with Python? The Complete Simulation Bootcamp will show you how simulation can transform your career and your projects.

Explore the Bootcamp