usim.py.resources – Synchronized Resources

μSim replicates all resource types provided by SimPy. Resources synchronize processes by sharing or exchanging objects, data, or ownership.

resource

Resources with a fixed capacity of usage slots

container

Resources with capacity of continuous, indistinguishable content

store

Resources with a fixed capacity of slots for storing arbitrary objects

base

Common interface inherited by all resource types

Just like the usim.py, usim.py.resources is a drop-in replacement for simpy.resources. It is possible to use usim.py resources both in usim.py processes as well as regular usim activities.

μSim

async def block(resource: Resource):
    with resource.get() as request:
        await request
        await (time + 5)

SimPy

def block(env, resource: Resource):
    with resource.get() as request:
        yield request
        yield env.timeout(5)

Note

μSim only replicates the public, documented API of SimPy’s resources. This includes private methods intended to be overridden, such as _do_put(). Implementation details, especially of internal methods such as _trigger_put(), may differ from SimPy.

Common Resource Interface

All resources of usim.py derive from a common interface: the BaseResource with a given capacity, and related events to put and get resource in or out of the resource.

class usim.py.resources.base.BaseResource(env: usim.py.core.Environment, capacity)[source]

Base class for all synchronised resources of usim.py

This type codifies the basic semantics of usim.py resources: processes can put() content into the resource or get() content out of the resource. Both actions return an Event; once the event triggers, the process did successfully get or put content into or out of the resource. Processes should yield this even to wait for success of their request.

def foo(env, resources: BaseResource):
    print('Getting a resource at', env.time)
    yield resources.get()
    print('Got resource at', env.time)
    yield env.timeout(2)
    print('Returning a resource at', env.time)
    yield resources.put()
    print('Returned a resource at', env.time)

Subclasses should define their behaviour by implementing at least one of:

PutQueue or GetQueue

The types used to create the put_queue and get_queue. These may, for example, customize priority of queued requests.

put() or get()

The methods used to create Put and Get events. These may, for example, customize how much of a resource to handle at once.

_do_put() or _do_get()

The methods used to process Put and Get events. These are an alternative to customizing put() or get().

Note

This is not an Abstract Base Class. Subclasses do not need to implement _do_get() or _do_put().

Hint

Migrating to μSim

There is no common base type for resources in μSim – instead there are several different types of resources made for various use-cases. If you need to create a custom resource, you are free to choose whatever interface is appropriate for your use-case.

μSim itself usually uses the await, async for and async with depending on the intended use of a resource. Commonly this means await resource to get content from a resource, await resource.put(...) to add content to a resource, async for item in resource: to subscribe to a resource, and async with resource: to temporarily use a resource.

_do_put(get_event: usim.py.resources.base.Put) bool[source]

Trigger a Put event if possible

Parameters

get_event – the event that may be triggered

Returns

whether another event may be triggered

_do_get(get_event: usim.py.resources.base.Get) bool[source]

Trigger a Get event if possible

Parameters

get_event – the event that may be triggered

Returns

whether another event may be triggered

GetQueue

alias of list

PutQueue

alias of list

property capacity

Maximum capacity of the resource

get() usim.py.resources.base.Get[usim.py.resources.base.T][source]

Create a request to get content out of the resource

get_queue

pending get events

put() usim.py.resources.base.Put[usim.py.resources.base.T][source]

Create a request to put content into the resource

put_queue

pending put events

Each request to put or get resources is represented by an Event. It is possible, but not necessary, for a process/activity to yield/await such a request to wait for its success.

class usim.py.resources.base.BaseRequest(resource: usim.py.resources.base.BaseResource)[source]

Base class for Put and Get events

A request is commonly created via put() or get() and must be yielded by a Process to wait until the request has been served. If a request has not been served but is no longer desired, the process should cancel the request.

A request can be used as a context manager to automatically cancel it at the end of the context. Note that the request is not automatically waited on – this must be done explicitly if desired.

with resource.get() as request:
    yield request
cancel()[source]

Cancel the request

Requests should be cancelled when they have not been triggered but are no longer needed. This may happen when a process is interrupted while waiting for a request.

Cancelling is idempotent: If the request has already been triggered or canceled, cancel() can still be called without error.

proc

the process that requested the action

class usim.py.resources.base.Put(resource: usim.py.resources.base.BaseResource)[source]

Request to put content into a resource

cancel()[source]

Cancel the request

Requests should be cancelled when they have not been triggered but are no longer needed. This may happen when a process is interrupted while waiting for a request.

Cancelling is idempotent: If the request has already been triggered or canceled, cancel() can still be called without error.

class usim.py.resources.base.Get(resource: usim.py.resources.base.BaseResource)[source]

Request to get content out of a resource

cancel()[source]

Cancel the request

Requests should be cancelled when they have not been triggered but are no longer needed. This may happen when a process is interrupted while waiting for a request.

Cancelling is idempotent: If the request has already been triggered or canceled, cancel() can still be called without error.

Resources – Shared Resource Usage

The Resource types implement various forms of semaphores: a pool of usage slots which can be requested, acquired and released. Variants of the basic Resource support request priorities (PriorityResource) and preemption (PreemptiveResource).

See also

Resource requests model temporary transfer of ownership: every request() is matched by an eventual release() of the resource. In contrast, a Container models permanent transfer of ownership.

Note

Resource types do not support the methods put() or get() but provide request() or release() instead.

class usim.py.resources.resource.Resource(env: usim.py.core.Environment, capacity: int = 1)[source]

Resource with a fixed capacity of usage slots

A process may request() a single usage slot, which is granted as soon as it becomes available. When all slots are taken, each further request() is queued until a previous request is release()d.

Warning

Both put() and get() cannot be used on this resource type.

property count: int

Number of Requests currently granted for the resource.

get()[source]

Not supported, use release() instead

put()[source]

Not supported, use request() instead

property queue: List[usim.py.resources.resource.Request]

Pending Requests currently waiting for the resource.

release(request: usim.py.resources.resource.Request) usim.py.resources.resource.Release[source]

Release a previous usage request of a resource

request() usim.py.resources.resource.Request[source]

Request usage of a resource

users

All Requests currently granted for the resource.

class usim.py.resources.resource.PriorityResource(env: usim.py.core.Environment, capacity: int = 1)[source]

Resource with a fixed capacity of usage slots granted with priorities

A process may request() a single usage slot, which is granted as soon as it becomes available. When all slots are taken, each further request() is queued until a previous request is release()d and no request of better priority is queued.

PutQueue

alias of usim.py.resources.resource.SortedQueue

request(priority=0) usim.py.resources.resource.PriorityRequest[source]

Request usage of a resource with a given priority

class usim.py.resources.resource.PreemptiveResource(env: usim.py.core.Environment, capacity: int)[source]

Resource with a fixed capacity of usage slots preempted with priorities

A process may request() a single usage slot, which is granted as soon as it becomes available. When all slots are taken, each further request() may preempt already granted request()s of worse priority. Otherwise, the request is queued until a previous request is release()d and no request of better priority is queued.

users

All Requests currently granted for the resource.

class usim.py.resources.resource.Request(resource: usim.py.resources.base.BaseResource)[source]

Request usage of a resource

This event is triggered once the request has been granted by the resource. If the resource has sufficient capacity, the request is granted immediately. Otherwise, the request is delayed until another request is released.

A request should always be either cancelled before being granted or released after being granted. A request can be used in a with statement to automatically cancel or release it as appropriate at the end of the context. Note that success of the usage request is not automatically waited on – this must be done explicitly if desired.

with resource.request() as request:
    yield request
# request is no longer held here
class usim.py.resources.resource.Release(resource, request)[source]

Release a previous request of a resource

class usim.py.resources.resource.PriorityRequest(resource, priority: float = 0, preempt=True)[source]

Request usage of a resource with a given priority

Parameters
  • priority – order of this request; smaller is chosen first

  • preempt – whether to replace a request that has worse priority if the resource is congested

By default, the ordering of priority of requests is determined by their priority, then creation time, then whether they preempt. Requests with smaller values and preempt=True are chosen first.

key

sort key of this resource - requests with smaller key are chosen first

priority

priority of this request, lower is chosen first

time

time at which the request was made

usage_since

time at which the request was granted

class usim.py.resources.resource.Preempted(by: usim.py.events.Process, usage_since: float, resource: usim.py.resources.resource.PreemptiveResource)[source]

Information on a preemption, carried as the cause of an Interrupt

A process which did successfully request() a resource may be preempted by a request of better priority. The initial process then receives a Interrupt, whose cause is an instance of this class.

by

process that triggered the preemption

resource

the resource that was lost

usage_since

time since when the resource was used

Containers – Continuous Resource Exchange

A Container models the exchange of resources between processes: processes may produce resources and put() them into the container, or consume resources and get() them out of the container. These resources may be continuous (i.e. fractions can be exchanged) and do not need to be conserved.

See also

Container requests model permanent transfer of ownership: processes may freely get() and put() content in and out of the container. In contrast, a Resource models temporary transfer of ownership.

class usim.py.resources.container.Container(env, capacity: float = inf, init: float = 0)[source]

Resource with capacity of continuous, indistinguishable content

A Process may get() out or put() in an arbitrary amount of content, provided the level or capacity suffices. Requests that cannot be granted immediately are served once sufficient level or capacity are available.

Parameters
  • capacity – the maximum amount of content available

  • init – the initial level of content available

By default, a Container has infinite capacity but starts empty. Note that it is not possible to have a higher initial content than maximum capacity.

Hint

Migrating to μSim

The closest equivalent of a Container in μSim are Resources. Each contains multiple resource capacities which can be increased, decreased, set, borrowed or claimed individually or together. To emulate a Container, use Resources with a single resource type:

resources = Resources(level=8)
# put some content into the resource
await resource.increase(level=8)
# get some content from the resource
await resource.decrease(level=8)

It is always safe to increase(), decrease() or set() resources – there is no need to cancel requests. To avoid leaking resources by not returning them, it is recommended to either borrow() or claim() resources – this automatically returns the resources at the end of a scope.

resources = Resources(apples=8, oranges=12)
# borrow resources when available, returning them automatically
async with resources.borrow(apples=2) as borrowed:
    print(f'temporarily got {borrowed.levels.apples} apples!')

When temporarily holding resources, this is represented by yet another resources instance. It can be passed around and further divided – but is guaranteed never to exceed what was taken from the initial resource.

get(amount=1) usim.py.resources.container.ContainerGet[source]

Get amount of content out of the container

property level: float

The current amount of available content in the container

put(amount=1) usim.py.resources.container.ContainerPut[source]

Put amount of content into the container

class usim.py.resources.container.ContainerPut(container: usim.py.resources.container.Container, amount: float)[source]

Request to put amount of resources into the container

class usim.py.resources.container.ContainerGet(container: usim.py.resources.container.Container, amount: float)[source]

Request to get amount of resources out of the container

Stores – Distinct Object Exchange

The Store types implement various forms of queues: an ordered container from which items can be removed and added. Variants of the basic Store support item priorities (PriorityStore) and item filtering (FilterStore).

class usim.py.resources.store.Store(env, capacity=inf)[source]

Resource with a fixed capacity of slots for storing arbitrary objects

A process can put() in specific items, provided there is enough capacity. A process can get() the next available item out of the store, provided there are any in the store. If the capacity is reached or if there are no items, the respective request is delayed.

The Store serves objects in first-in-first-out order.

Hint

Migrating to μSim

To pass items between activities, use a Queue. Queues store items for later retrieval:

queue = Queue()
# put an item into the queue
await queue.put(1)
# get an item out of the queue
item = await queue

In addition to adding or retrieving individual items, it is possible to subscribe to a queue by iteration:

async for item in queue:
    print(item)

Even with several subscribers, a Queue yields every item only once.

get() usim.py.resources.store.StoreGet[usim.py.resources.store.T][source]

Get an item out of the store

property items: List[usim.py.resources.store.T]

The currently available items in the store

put(item: usim.py.resources.store.T) usim.py.resources.store.StorePut[usim.py.resources.store.T][source]

Put an item into the store

class usim.py.resources.store.PriorityStore(env, capacity=inf)[source]

Resource with a fixed capacity of slots for storing arbitrary objects in order

The items of the store are maintained in sorted order, with smaller items stored and served first. All items in the store must support a < b comparisons. To store unorderable items, use PriorityItem.

class usim.py.resources.store.PriorityItem(priority: float, item: Any)[source]

Helper to sort an unorderable item by a priority

This class implements a total ordering based on priority; item is ignored for comparisons. This allows using an arbitrary item in a PriorityStore with a well-defined priority.

The original simpy.resources.store.PriorityItem only properly provides a < b ordering; all other comparisons may compare item. usim.py provides well-defined total ordering.

property item

Alias for field number 1

property priority

Alias for field number 0

class usim.py.resources.store.FilterStore(env, capacity=inf)[source]

Resource with a fixed capacity of slots for storing arbitrary objects

Requests to get() items support a filter, which limits the objects that satisfy the request. A request may not be satisfied if the store contains only items that do not match the request filter. Pending requests remain queued until a valid item appears in the store.

While requests are served in first-in-first-out order, they are not granted in first-in-first-out order if items fail the filter of earlier requests. In addition, a request has to inspect all items in the store to conclude it cannot be granted. In the worst case, a store with n items and m request takes O(mn) to get or put items.

get(filter: Callable[[usim.py.resources.store.T], bool] = <function accept_any>) usim.py.resources.store.FilterStoreGet[usim.py.resources.store.T][source]

Get an item out of the store that satisfies filter

class usim.py.resources.store.StoreGet(resource: usim.py.resources.base.BaseResource)[source]

Request to get an item out of the resource

class usim.py.resources.store.StorePut(store: usim.py.resources.store.Store, item: usim.py.resources.store.T)[source]

Request to put an item into the store

class usim.py.resources.store.FilterStoreGet(resource, filter: Callable[[usim.py.resources.store.T], bool] = <function FilterStoreGet.<lambda>>)[source]

Request to get an item out of the resource if it passes filter

The filter function is applied to all items of a store, and the first for which filter(item) returns True is the result.