Skip to content

michalc/fifolock

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
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.

Installation

pip install fifolock

Recipes

Mutex (exclusive) lock

import asyncio
from fifolock import FifoLock


class Mutex(asyncio.Future):
    @staticmethod
    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):
    @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]


lock = FifoLock()

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

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

Semaphore

import asyncio
from fifolock import FifoLock


class SemaphoreBase(asyncio.Future):
    @classmethod
    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 setup.py 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.

About

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

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages