Events, Timers, and Signals

Most models can be written with process blocking calls. Explicit events, process timers, and signal handling are useful when a model needs direct wakeups, cancellation, deadlines, or scheduled callbacks.

Explicit events

Declare a sim.Event field and register a matching @model.event callback:

class Clinic(sim.Model):
    close_shift: sim.Event
    arrivals: sim.Processes
    closed: sim.State


model = Clinic("clinic")


@model.event
def close_shift(env: Clinic):
    env.closed = 1
    sim.stop(env.arrivals[0], 0)


@model.process
def supervisor(env: Clinic):
    sim.schedule(env.close_shift, env, 480.0)
    sim.suspend()

sim.schedule() uses a delay from the current time. sim.schedule_at() uses an absolute simulation time. Scheduled event handles can be cancelled, rescheduled, reprioritized, inspected, and waited on.

Waiting on scheduled events

An event can be used as a deadline that another process waits for:

@model.process
def reminder(env: Clinic):
    handle = sim.schedule(env.close_shift, env, 480.0)
    sig = sim.wait_event(handle)
    if sig == sim.SUCCESS:
        env.closed = 1

If the event is cancelled before it fires, sim.wait_event() returns a non-success signal. Check the signal when cancellation changes the model path.

Process timers

Timers wake a specific process. They are a natural fit for impatience, timeouts, appointment no-shows, and retry deadlines:

TIMER_PATIENCE = 17


@model.process
def patient(env: Clinic, p: Patient):
    me = sim.current()
    sim.timer_set(me, p.patience, TIMER_PATIENCE)
    sig = sim.suspend()
    if sig == TIMER_PATIENCE:
        # The patient waited too long.
        return
    sim.timers_clear(me)
    # The patient was resumed by service before the timer fired.

sim.timer_set() clears existing timers before adding one. sim.timer_add() adds another independent timer. sim.timer_cancel() cancels one timer handle, and sim.timers_clear() clears all timers for a process.

Signals and cleanup

Blocking calls return signals. sim.SUCCESS means the operation completed normally. Other values can indicate timeout, interruption, stop, cancellation, or preemption:

import cimba.random as random

sig = env.doctor.acquire()
if sig != sim.SUCCESS:
    return

try:
    sig = sim.hold(random.exponential(env.mean_service))
    if sig == sim.SUCCESS:
        env.completed += 1
finally:
    if env.doctor.held(sim.current()):
        env.doctor.release()

Treat every blocking call as a possible handoff point. Another process may interrupt this process, stop it, preempt held capacity, or resume it with a domain-specific signal before it runs again.

Use explicit events and timers when they make the model rule clearer. If a normal sim.hold(), queue operation, resource acquire, or condition wait expresses the rule directly, prefer the simpler blocking operation.

For process fundamentals, see Processes and Simulated Time.