School of Simulation Logo - Navigate to Homepage

Trustpilot Reviews

← All articles

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:

EventWhat It Does
TimeoutFires after a duration
ProcessFires when process completes
EventManual trigger
AnyOfFires when any event occurs
AllOfFires 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:

  • Pending - Not yet triggered
  • Triggered - Will fire at current time
  • Processed - Has fired and been handled

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