This post is about cocotb Lock
trigger which is used as a mutex between cocotb coroutines.
example Link to heading
The typical use case is locking while writing by defining the lock and using async with
.
self.write_address_busy = Lock()
async with self.write_address_busy:
...
Deep dive Link to heading
Looking at the code for Lock
, the docstring has the description and equivalent code for Lock.
class Lock(AsyncContextManager[None]):
"""A mutual exclusion lock.
Guarantees fair scheduling.
Lock acquisition is given in order of attempted lock acquisition.
Usage:
By directly calling :meth:`acquire` and :meth:`release`.
.. code-block:: python
lock = Lock()
...
await lock.acquire()
try:
# do some stuff
...
finally:
lock.release()
Or...
.. code-block:: python
async with Lock():
# do some stuff
...
The lock can be used as an asynchronous context manager in an
:keyword:`async with` statement
As expected, there are __aenter__
and __aexit__
which call internal acquire and release.
async def __aenter__(self) -> None:
await self.acquire()
async def __aexit__(self, *args: object) -> None:
self.release()
when released, it pops from _pending_primed
and things are delegated to _Lock
def _acquire_and_fire(self, lock: _Lock) -> None:
self._locked = True
lock._callback(lock)
...
...
def acquire(self) -> Trigger:
"""Produce a trigger which fires when the lock is acquired."""
trig = _Lock(self)
return trig
def release(self) -> None:
"""Release the lock."""
if not self._locked:
raise RuntimeError(f"Attempt to release an unacquired Lock {self!s}")
self._locked = False
# nobody waiting for this lock
if not self._pending_primed:
return
lock = self._pending_primed.pop(0)
self._acquire_and_fire(lock)