Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
extmod/uasyncio: Fix race with cancelled task waiting on finished task.
This commit fixes a problem with a race between cancellation of task A and completion of task B, when A waits on B. If task B completes just before task A is cancelled then the cancellation of A does not work. Instead, the CancelledError meant to cancel A gets passed through to B (that's expected behaviour) but B handles it as a "Task exception wasn't retrieved" scenario, printing out such a message (this is because finished tasks point their "coro" attribute to themselves to indicate they are done, and implement the throw() method, but that method inadvertently catches the CancelledError). The correct behaviour is for B to bounce that CancelledError back out. This bug is mainly seen when wait_for() is used, and in that context the symptoms are: - occurs when using wait_for(T, S), if the task T being waited on finishes at exactly the same time as the wait-for timeout S expires - task T will have run to completion - the "Task exception wasn't retrieved message" is printed with "<class 'CancelledError'>" as the error (ie no traceback) - the wait_for(T, S) call never returns (it's never put back on the uasyncio run queue) and all tasks waiting on this are blocked forever from running - uasyncio otherwise continues to function and other tasks continue to be scheduled as normal The fix here reworks the "waiting" attribute of Task to be called "state" and uses it to indicate whether a task is: running and not awaited on, running and awaited on, finished and not awaited on, or finished and awaited on. This means the task does not need to point "coro" to itself to indicate finished, and also allows removal of the throw() method. A benefit of this is that "Task exception wasn't retrieved" messages can go back to being able to print the name of the coroutine function. Fixes issue #7386. Signed-off-by: Damien George <damien@micropython.org>
- Loading branch information
Showing
5 changed files
with
111 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Test cancelling a task that is waiting on a task that just finishes. | ||
|
||
try: | ||
import uasyncio as asyncio | ||
except ImportError: | ||
try: | ||
import asyncio | ||
except ImportError: | ||
print("SKIP") | ||
raise SystemExit | ||
|
||
|
||
async def sleep_task(): | ||
print("sleep_task sleep") | ||
await asyncio.sleep(0) | ||
print("sleep_task wake") | ||
|
||
|
||
async def wait_task(t): | ||
print("wait_task wait") | ||
await t | ||
print("wait_task wake") | ||
|
||
|
||
async def main(): | ||
waiting_task = asyncio.create_task(wait_task(asyncio.create_task(sleep_task()))) | ||
|
||
print("main sleep") | ||
await asyncio.sleep(0) | ||
print("main sleep") | ||
await asyncio.sleep(0) | ||
|
||
waiting_task.cancel() | ||
print("main wait") | ||
try: | ||
await waiting_task | ||
except asyncio.CancelledError as er: | ||
print(repr(er)) | ||
|
||
|
||
asyncio.run(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
main sleep | ||
sleep_task sleep | ||
wait_task wait | ||
main sleep | ||
sleep_task wake | ||
main wait | ||
CancelledError() |