Skip to content
Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

fifolock CircleCI Maintainability Test Coverage

A flexible low-level tool to make synchronisation primitives in asyncio Python. As the name suggests, locks are granted strictly in the order requested: first-in-first-out; and are not reentrant.


pip install fifolock


Mutex (exclusive) lock

import asyncio
from fifolock import FifoLock

class Mutex(asyncio.Future):
    def is_compatible(holds):
        return not holds[Mutex]

lock = FifoLock()

async def access():
    async with lock(Mutex):
        # access resource

Read/write (shared/exclusive) lock

import asyncio
from fifolock import FifoLock

class Read(asyncio.Future):
    def is_compatible(holds):
        return not holds[Write]

class Write(asyncio.Future):
    def is_compatible(holds):
        return not holds[Read] and not holds[Write]

lock = FifoLock()

async def read():
    async with lock(Read):
        # shared access

async def write():
    async with lock(Write):
        # exclusive access


import asyncio
from fifolock import FifoLock

class SemaphoreBase(asyncio.Future):
    def is_compatible(cls, holds):
        return holds[cls] < cls.size

lock = FifoLock()
Semaphore = type('Semaphore', (SemaphoreBase, ), {'size': 3})

async def access():
    async with lock(Semaphore):
        # at most 3 concurrent accesses

Running tests

python test

Design choices

Each mode of the lock is a subclass of asyncio.Future. This could be seen as a leak some of the internals of FifoLock, but it allows for clear client and internal code.

  • Classes are hashable, so each can be a key in the holds dictionary passed to the is_compatible method. This allows the compatibility conditions to be read clearly in the client code, and the holds dictionary to be mutated clearly internally.

  • An instance of it, created inside FifoLock, is both the object awaited upon, and stored in a deque with a way of accessing its is_compatible method.

  • The fact it's a class and not an instance of a class also makes clear it is to store no state, merely configuration.

A downside is that for configurable modes, such as for a semaphore, the client must dynamically create a class: this is not a frequently-used pattern.

The fact that the lock is not reentrant is deliberate: the class of algorithms this is designed for does not require this. This would add unnecessary complexity, and presumably be slower.


A flexible low-level tool to make synchronisation primitives in asyncio Python




No packages published