aiologic is a locking library for tasks synchronization and their communication. It provides primitives that are both async-aware and thread-aware, and can be used for interaction between:
- async codes (async <-> async) in one thread as regular async primitives
- async codes (async <-> async) in multiple threads (!)
- async code and sync one (async <-> sync) in one thread (!)
- async code and sync one (async <-> sync) in multiple threads (!)
- sync codes (sync <-> sync) in one thread as regular sync primitives
- sync codes (sync <-> sync) in multiple threads as regular sync primitives
Let's take a look at the example:
import asyncio
from threading import Thread
import aiologic
lock = aiologic.Lock()
async def func(i: int, j: int) -> None:
print(f"thread={i} task={j} start")
async with lock:
await asyncio.sleep(1)
print(f"thread={i} task={j} end")
async def main(i: int) -> None:
await asyncio.gather(func(i, 0), func(i, 1))
Thread(target=asyncio.run, args=[main(0)]).start()
Thread(target=asyncio.run, args=[main(1)]).start()
It prints something like this:
thread=0 task=0 start thread=1 task=0 start thread=0 task=1 start thread=1 task=1 start thread=0 task=0 end thread=1 task=0 end thread=0 task=1 end thread=1 task=1 end
As you can see, tasks from different event loops are all able to acquire
aiologic.Lock
. In the same case if you use asyncio.Lock
,
it will raise a RuntimeError
. And threading.Lock
will cause a deadlock.
- Python 3.8+ support
- CPython and PyPy support
- Pickling and weakrefing support
- Cancellation and timeouts support
- Optional Trio-style checkpoints:
- enabled by default for Trio itself
- disabled by default for all others
- Only one checkpoint per asynchronous call:
- exactly one context switch if checkpoints are enabled
- zero or one context switch if checkpoints are disabled
- Fairness wherever possible (with some caveats)
- Thread-safety wherever possible
- Lock-free implementation
- Bundled stub files
Synchronization primitives:
- Semaphores: counting, and bounded
- Locks: primitive, ownable, and reentrant
- Capacity limiters: simple, and reentrant
- Condition variables
- Barriers: single-use, and cyclic
- Events: one-time, reusable, and countdown
- Resource guards
Communication primitives:
- Queues: FIFO, LIFO, and priority
Supported concurrency libraries:
All synchronization and communication primitives are implemented entirely
on effectively atomic operations, which gives an incredible speedup on PyPy
compared to alternatives from the threading
module.
All this works because of GIL, but per-object locks also ensure that
the same operations are still atomic,
so aiologic also works when running in a free-threaded mode.
Install from PyPI (recommended):
pip install aiologic
Or from GitHub:
pip install git+https://github.com/x42005e1f/aiologic.git
You can also use other package managers, such as uv.
Read the Docs: https://aiologic.readthedocs.io
GitHub Discussions: https://github.com/x42005e1f/aiologic/discussions
Feel free to post your questions and ideas here.
If you like aiologic and want to support its development, star its repository on GitHub.
The aiologic library is REUSE-compliant and is offered under multiple licenses:
- All original source code is licensed under ISC.
- All original test code is licensed under 0BSD.
- All documentation is licensed under CC-BY-4.0.
- All configuration is licensed under CC0-1.0.
For more accurate information, check the individual files.