← Về thư mục
📄 / / profiles / r-and-d / raw_evidence / scientific-agent-skills / skills / simpy / references / resources.md

Shared resources

Verified 2026-07-23 against SimPy 4.1.2.

All resource operations return Events. An operation that cannot complete waits in the corresponding queue; yielding the Event suspends the process. Resource events are context managers so exception/interrupt unwinding can release an acquired request or cancel a pending operation.

Choose by modeled quantity

Primitive Modeled state Waiting operation
Resource limited concurrent users request()
PriorityResource users plus priority queue request(priority=...)
PreemptiveResource priority users that may be displaced request(..., preempt=...)
Container homogeneous numeric level put(amount), get(amount)
Store FIFO concrete objects put(item), get()
FilterStore objects selected by predicate get(filter)
PriorityStore comparable/priority-wrapped objects put(item), get()

These primitives implement synchronization and congestion. They do not decide whether a queue discipline or capacity assumption is valid for the real system.

Resource

Resource(env, capacity=1) is semaphore-like. capacity must be positive. Observable state:

import simpy

def user(env, resource, duration):
    with resource.request() as request:
        yield request
        yield env.timeout(duration)

env = simpy.Environment()
server = simpy.Resource(env, capacity=2)
for duration in (2, 3, 4):
    env.process(user(env, server, duration))
env.run()

The context manager calls release(request) after an acquired request and cancel() for an abandoned pending request. With manual management, always release the exact request token:

request = resource.request()
try:
    yield request
    yield env.timeout(2)
finally:
    if request in resource.users:
        resource.release(request)
    elif not request.triggered:
        request.cancel()

release() succeeds immediately, even if passed a request that is not a current user; such a call does not make the model logically correct. Prefer the context manager.

PriorityResource

PriorityResource orders pending PriorityRequests by smaller numeric priority, then request time. Equal-priority, equal-time ties use deterministic scheduler/queue ordering.

def prioritized(env, resource, name, priority):
    with resource.request(priority=priority) as request:
        yield request
        print(name, "started", env.now)
        yield env.timeout(2)

env = simpy.Environment()
resource = simpy.PriorityResource(env, capacity=1)
env.process(prioritized(env, resource, "routine", 10))
env.process(prioritized(env, resource, "urgent", 0))
env.run()

Priority does not preempt a current user. A later high-priority request only overtakes queued lower-priority requests.

PreemptiveResource

PreemptiveResource extends PriorityResource. A request accepts:

def task(env, resource, name, priority, duration):
    with resource.request(priority=priority, preempt=True) as request:
        try:
            yield request
            started = env.now
            yield env.timeout(duration)
        except simpy.Interrupt as interrupt:
            cause = interrupt.cause
            used = env.now - cause.usage_since
            print(name, "preempted by", cause.by.name, "after", used)

env = simpy.Environment()
cpu = simpy.PreemptiveResource(env, capacity=1)
env.process(task(env, cpu, "background", 5, 10))

def urgent(env):
    yield env.timeout(2)
    yield env.process(task(env, cpu, "urgent", 0, 1))

env.process(urgent(env))
env.run()

The preempted process receives simpy.Interrupt. Its cause is simpy.resources.resource.Preempted:

The timeout representing interrupted work is not canceled; the process's callback is removed from it. Track completed work and schedule a new timeout to resume.

Priority outranks the preempt flag. A queued request with higher priority and preempt=False can prevent a lower-priority preempting request from displacing a user. Do not mix policies without a tested discipline specification.

Container

Container(env, capacity=float("inf"), init=0) models homogeneous matter. Current state is level.

env = simpy.Environment()
tank = simpy.Container(env, capacity=100, init=40)

def transfer(env):
    yield tank.put(30)
    assert tank.level == 70
    yield tank.get(20)
    assert tank.level == 50

env.process(transfer(env))
env.run()

Pending operations are visible through put_queue and get_queue. A blocked put or get can deadlock the model if no future process can change the level. Bound execution and assert conservation:

initial + completed_puts - completed_gets == final_level.

Store

Store(env, capacity=float("inf")) holds concrete Python objects. put(item) waits when full; get() waits when empty. Available objects are in items; pending operations are in put_queue and get_queue.

env = simpy.Environment()
buffer = simpy.Store(env, capacity=2)

def producer(env):
    for item in ("a", "b", "c"):
        yield buffer.put(item)

def consumer(env):
    for _ in range(3):
        item = yield buffer.get()
        print(item)

env.process(producer(env))
env.process(consumer(env))
env.run()

Store is FIFO for available items and ordinary pending requests.

FilterStore

FilterStore.get(predicate) takes the first available item for which the predicate returns truthy:

env = simpy.Environment()
machines = simpy.FilterStore(env, capacity=2)
machines.items = [
    {"id": "small", "size": 1},
    {"id": "large", "size": 2},
]

def borrow_large(env):
    machine = yield machines.get(lambda item: item["size"] >= 2)
    yield env.timeout(1)
    yield machines.put(machine)

env.process(borrow_large(env))
env.run()

Unlike a plain FIFO get queue, a later FilterStore request can complete before an earlier request when only the later predicate matches. Predicates execute inside resource processing: keep them deterministic, fast, side-effect-free, and defined in trusted model code. The bundled JSON CLIs do not accept predicate code.

PriorityStore

PriorityStore returns the smallest item according to comparison. Prefer simpy.PriorityItem(priority, item) so payloads need not be comparable:

env = simpy.Environment()
work = simpy.PriorityStore(env)

def schedule(env):
    yield work.put(simpy.PriorityItem(10, "routine"))
    yield work.put(simpy.PriorityItem(1, "urgent"))
    first = yield work.get()
    assert first.item == "urgent"

env.process(schedule(env))
env.run()

Equal-priority payloads preserve the wrapper's comparison behavior; if business policy requires a specific tie-breaker, include one explicitly in a comparable dataclass/tuple and test it.

Waiting, cancellation, and reneging

Use a condition to race a request against patience:

def customer(env, resource, patience):
    with resource.request() as request:
        result = yield request | env.timeout(patience)
        if request not in result:
            return "reneged"  # Pending request canceled on context exit.
        yield env.timeout(2)
        return "served"

If retaining resource events outside a context manager:

Monitoring caveat

Resource state may change synchronously when an operation is created and again when its callbacks are processed. Label whether a sample is pre-call, post-call, grant, release, or final. Time-weight state paths; do not average event samples. See monitoring.md.

Sources

See sources.md for the 4.1.2 shared-resource guide and API reference.