Skip to content

Instantly share code, notes, and snippets.

@michalc
Last active January 3, 2022 09:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michalc/ab9bd571cfab09216c0316f2302a76b0 to your computer and use it in GitHub Desktop.
Save michalc/ab9bd571cfab09216c0316f2302a76b0 to your computer and use it in GitHub Desktop.
Python asyncio read-write lock, using a generic first-in-first-out lock
import asyncio
import collections
import contextlib
class Read(asyncio.Future):
@staticmethod
def is_compatible(holds):
return not holds[Write]
class Write(asyncio.Future):
@staticmethod
def is_compatible(holds):
return not holds[Read] and not holds[Write]
class FifoLock():
def __init__(self):
self._waiters = collections.deque()
self._holds = collections.defaultdict(int)
def _maybe_acquire(self):
while True:
if not self._waiters:
break
if self._waiters[0].cancelled():
self._waiters.popleft()
continue
if self._waiters[0].is_compatible(self._holds):
waiter = self._waiters.popleft()
self._holds[type(waiter)] += 1
waiter.set_result(None)
continue
break
@contextlib.asynccontextmanager
async def __call__(self, lock_mode_type):
lock_mode = lock_mode_type()
self._waiters.append(lock_mode)
self._maybe_acquire()
try:
await lock_mode
except asyncio.CancelledError:
self._maybe_acquire()
raise
try:
yield
finally:
self._holds[type(lock_mode)] -= 1
self._maybe_acquire()
@michalc
Copy link
Author

michalc commented Jan 3, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment