/usr/lib/python3/dist-packages/simpy/resources/base.py is in python3-simpy3 3.0.10-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | """
Base classes of for Simpy's shared resource types.
:class:`BaseResource` defines the abstract base resource. It supports *get* and
*put* requests, which return :class:`Put` and :class:`Get` events respectively.
These events are triggered once the request has been completed.
"""
from simpy.core import BoundClass
from simpy.events import Event
class Put(Event):
"""Generic event for requesting to put something into the *resource*.
This event (and all of its subclasses) can act as context manager and can
be used with the :keyword:`with` statement to automatically cancel the
request if an exception (like an :class:`simpy.events.Interrupt` for
example) occurs:
.. code-block:: python
with res.put(item) as request:
yield request
"""
def __init__(self, resource):
super(Put, self).__init__(resource._env)
self.resource = resource
self.proc = self.env.active_process
resource.put_queue.append(self)
self.callbacks.append(resource._trigger_get)
resource._trigger_put(None)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.cancel()
def cancel(self):
"""Cancel this put request.
This method has to be called if the put request must be aborted, for
example if a process needs to handle an exception like an
:class:`~simpy.events.Interrupt`.
If the put request was created in a :keyword:`with` statement, this
method is called automatically.
"""
if not self.triggered:
self.resource.put_queue.remove(self)
class Get(Event):
"""Generic event for requesting to get something from the *resource*.
This event (and all of its subclasses) can act as context manager and can
be used with the :keyword:`with` statement to automatically cancel the
request if an exception (like an :class:`simpy.events.Interrupt` for
example) occurs:
.. code-block:: python
with res.get() as request:
item = yield request
"""
def __init__(self, resource):
super(Get, self).__init__(resource._env)
self.resource = resource
self.proc = self.env.active_process
resource.get_queue.append(self)
self.callbacks.append(resource._trigger_put)
resource._trigger_get(None)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.cancel()
def cancel(self):
"""Cancel this get request.
This method has to be called if the get request must be aborted, for
example if a process needs to handle an exception like an
:class:`~simpy.events.Interrupt`.
If the get request was created in a :keyword:`with` statement, this
method is called automatically.
"""
if not self.triggered:
self.resource.get_queue.remove(self)
class BaseResource(object):
"""Abstract base class for a shared resource.
You can :meth:`put()` something into the resources or :meth:`get()`
something out of it. Both methods return an event that is triggered once
the operation is completed. If a :meth:`put()` request cannot complete
immediately (for example if the resource has reached a capacity limit) it
is enqueued in the :attr:`put_queue` for later processing. Likewise for
:meth:`get()` requests.
Subclasses can customize the resource by:
- providing custom :attr:`PutQueue` and :attr:`GetQueue` types,
- providing custom :class:`Put` respectively :class:`Get` events,
- and implementing the request processing behaviour through the methods
``_do_get()`` and ``_do_put()``.
"""
PutQueue = list
"""The type to be used for the :attr:`put_queue`. It is a plain
:class:`list` by default. The type must support index access (e.g.
``__getitem__()`` and ``__len__()``) as well as provide ``append()`` and
``pop()`` operations."""
GetQueue = list
"""The type to be used for the :attr:`get_queue`. It is a plain
:class:`list` by default. The type must support index access (e.g.
``__getitem__()`` and ``__len__()``) as well as provide ``append()`` and
``pop()`` operations."""
def __init__(self, env, capacity):
self._env = env
self._capacity = capacity
self.put_queue = self.PutQueue()
"""Queue of pending *put* requests."""
self.get_queue = self.GetQueue()
"""Queue of pending *get* requests."""
# Bind event constructors as methods
BoundClass.bind_early(self)
@property
def capacity(self):
"""Maximum capacity of the resource."""
return self._capacity
put = BoundClass(Put)
"""Request to put something into the resource and return a :class:`Put`
event, which gets triggered once the request succeeds."""
get = BoundClass(Get)
"""Request to get something from the resource and return a :class:`Get`
event, which gets triggered once the request succeeds."""
def _do_put(self, event):
"""Perform the *put* operation.
This method needs to be implemented by subclasses. If the conditions
for the put *event* are met, the method must trigger the event (e.g.
call :meth:`Event.succeed()` with an apropriate value).
This method is called by :meth:`_trigger_put` for every event in the
:attr:`put_queue`, as long as the return value does not evaluate
``False``.
"""
raise NotImplementedError(self)
def _trigger_put(self, get_event):
"""This method is called once a new put event has been created or a get
event has been processed.
The method iterates over all put events in the :attr:`put_queue` and
calls :meth:`_do_put` to check if the conditions for the event are met.
If :meth:`_do_put` returns ``False``, the iteration is stopped early.
"""
# Maintain queue invariant: All put requests must be untriggered.
# This code is not very pythonic because the queue interface should be
# simple (only append(), pop(), __getitem__() and __len__() are
# required).
idx = 0
while idx < len(self.put_queue):
put_event = self.put_queue[idx]
proceed = self._do_put(put_event)
if not put_event.triggered:
idx += 1
elif self.put_queue.pop(idx) != put_event:
raise RuntimeError('Put queue invariant violated')
if not proceed:
break
def _do_get(self, event):
"""Perform the *get* operation.
This method needs to be implemented by subclasses. If the conditions
for the get *event* are met, the method must trigger the event (e.g.
call :meth:`Event.succeed()` with an apropriate value).
This method is called by :meth:`_trigger_get` for every event in the
:attr:`get_queue`, as long as the return value does not evaluate
``False``.
"""
raise NotImplementedError(self)
def _trigger_get(self, put_event):
"""Trigger get events.
This method is called once a new get event has been created or a put
event has been processed.
The method iterates over all get events in the :attr:`get_queue` and
calls :meth:`_do_get` to check if the conditions for the event are met.
If :meth:`_do_get` returns ``False``, the iteration is stopped early.
"""
# Maintain queue invariant: All get requests must be untriggered.
# This code is not very pythonic because the queue interface should be
# simple (only append(), pop(), __getitem__() and __len__() are
# required).
idx = 0
while idx < len(self.get_queue):
get_event = self.get_queue[idx]
proceed = self._do_get(get_event)
if not get_event.triggered:
idx += 1
elif self.get_queue.pop(idx) != get_event:
raise RuntimeError('Get queue invariant violated')
if not proceed:
break
|