Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling asyncio deadlocks

This sample code hangs indefinitely:

import asyncio


async def main():
    async def f():
        await g_task

    async def g():
        await f_task

    f_task = asyncio.create_task(f())
    g_task = asyncio.create_task(g())
    await f_task


asyncio.run(main())

I'm looking for a way to automatically detect and handle deadlocks, like GoLang does.

So far I came up with a variant of asyncio.wait_for():

[EDIT] overhauled design

https://gist.github.com/gimperiale/549cbad04c24d870145d3f38fbb8e6f0

1 line change in the original code:

await wait_check_deadlock(f_task)

It works, but with two major problems:

  1. it relies on asyncio.Task._fut_waiter, which is an implementation detail of CPython
  2. The deadlocked tasks will remain in RAM forever. aw.cancel() seems to do nothing. If I catch the RecursionError my helper function raises, asyncio.run() raises another RecursionError when it tries cancelling all tasks.

Are there more robust solutions to the problem?

like image 865
crusaderky Avatar asked Mar 25 '19 15:03

crusaderky


People also ask

Is it possible to avoid deadlock in async code?

That would be the end of story but sometimes it cannot be avoided, and it’s not the only case. Deadlock might also be cause by other sort of blocking code, waiting for semaphore, acquiring as lock. The advice in general is simple. Don’t block in async code.

How to deal with deadlock?

Deadlocked processes are involved in a circular chain such that each process holds one or more resources being requested by the next process in the chain. There are three approaches to deal with deadlocks. 1. Deadlock Prevention 2. Deadlock avoidance 3. Deadlock detection These are explained as following below. 1. Deadlock Prevention :

What are the strategies used for deadlock handling in distributed system?

The following are the strategies used for Deadlock Handling in Distributed System: 1. Deadlock Prevention: As the name implies, this strategy ensures that deadlock can never happen because system designing is carried out in such a way. If any one of the deadlock-causing conditions is not met then deadlock can be prevented.

Should you use asyncio when developing a greenfields application?

When developing a new I/O bound application from scratch, asyncio may be a natural technology choice. Starting out, you’ll be able to use non-blocking libraries that work with asyncio such as asyncpg and aiohttp as you begin development. However, greenfields development is a luxury that many developers don’t have.


1 Answers

Deadlock avoidance has been researched a lot, some practical solutions exist, but in general case, the problem is undecidable (I think it can be reduced to the halting problem).

To illustrate practicality, consider this:

await asyncio.sleep(2 ** (1 / random.random()))

Depending on your luck, it will either return soon or "practically never".

This trick can be used to show that callback-based program is impossible to predict:

f = asyncio.Future()

async foo():
    await asyncio.sleep(2 ** (1 / random.random()))
    f.set_result(None)

async bar():
    await f

await asyncio.gather(foo(), bar())

Likewise, it can be applied to your "pure" async/await program:

async def f():
    await g_task

async def g():
    await asyncio.wait(f_task,
                       asyncio.sleep(2 ** (1 / random.random())),
                       return_when=asyncio.FIRST_COMPLETED)

f_task = asyncio.create_task(f())
g_task = asyncio.create_task(g())
await f_task

At the same time, imperfect but practical deadlock detector can be very useful, please consider posting your code to core asyncio devs and/or a standalone library.

The current practice is to run tests with PYTHONASYNCIODEBUG=1 which shows unawaited tasks (destroyed before result / exception was read).

Your library could be better, for example, it could report when some task took longer than X, or when a DAG of tasks depending on given task grows too large.

like image 172
Dima Tisnek Avatar answered Sep 22 '22 11:09

Dima Tisnek