Skip to main content Link Menu Expand (external link) Document Search Copy Copied

When Tremolo HTTP server is configured with worker_num > 1, it can be dangerous e.g. when you have code that writes to the same file.

server['lock'] can be used to mitigate this potential problem.

It can synchronize all tasks across multiple workers/processes. In single process mode, it will only be like asyncio.Lock, but thread-safe.

Example of using the asynchronous context manager with async with:

@app.route('/update')
async def update(**server):
    lock = server['lock']

    async with lock:
        # lock acquired, modify the shared resource
        # other processes that are acquiring this lock should be waiting

Or the traditional way:

@app.route('/update')
async def update(**server):
    lock = server['lock']

    try:
        await lock.acquire()
        # lock acquired, modify the shared resource
        # other processes that are acquiring this lock should be waiting
    finally:
        lock.release()

Multiple Shared Resources

In case you have multiple shared resources/different files, using a single lock as above can block other concurrent tasks - even if they are going to modify different files. It makes sense to have more than one lock provided for each resource for maximum concurrency.

Tremolo by default has 16 usable locks, 0 - 15 or 0x0 - 0xf.

# in current task
    lock = server['lock']

    async with lock(0):
        # lock acquired, modify the file1
        # other processes that are acquiring this lock(0) should be waiting

# in another concurrent task, independent from lock(0)
    lock = server['lock']

    async with lock(1):
        # lock acquired, modify the file2
        # other processes that are acquiring this lock(1) should be waiting

In the example above we found using two locks, lock(0) and lock(1), for two different files.

lock(0) is basically the same as lock, and if the given number exceeds the default range of 0 - 15, for example 16, it will be rotated using modulo internally. Meaning lock(16) will only be the same as lock(0) or lock.

If more than 16 locks are required, they can be set with the locks configuration.

This is a design decision; that locks cannot be added on-demand/dynamically. Otherwise the implementation will be more complex and likely to be overhead.