Title: Need to yield (sleep(0)) twice in asyncio
Type: behavior Stage:
Components: asyncio Versions: Python 3.7
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Assaf Dayan, asvetlov, yselivanov
Priority: normal Keywords:

Created on 2019-03-14 16:59 by Assaf Dayan, last changed 2019-04-19 08:42 by asvetlov.

File name Uploaded Description Edit Assaf Dayan, 2019-03-14 16:59 Python 3.7+ script which simulates the bug
Messages (2)
msg337950 - (view) Author: Assaf Dayan (Assaf Dayan) Date: 2019-03-14 16:59
In asyncio, 'await asyncio.sleep(0)' is used to relinquish control and check for finished awaitables before continuing. 
In my example, an awaitable returns but is only handled if 'await asyncio.sleep(0)' is called twice (adjacently) and only then the returned 'await' statements are handled correctly.

The attached script generates tasks which do the following: compute for 0.8 seconds; do an async op for 0.5 seconds; and compute some more for 0.2 seconds.
Before generating each new task, we relinquish control so that existing pending tasks will resume operation, if their 'await' statements returned.
We simulate a business-case in which we give precedence to already-processing tasks over new ones.

When running the attached script, Task 1 goes async at d=0.8 and should be ready by d=1.3 (asyncio.sleep(0.5)). Because Task 2 started at d=0.8 (when T1 went async) it will relinquish control at d=1.6. By relinquishing control at d=1.6, Task 1 should resume, but it doesn't, unless asyncio.sleep(0) is called once more.
If asyncio.sleep(0) is not called again (as a fix), Task 1 will only resume at d=2.4, which is after Task 3 relinquished control.
msg340530 - (view) Author: Andrew Svetlov (asvetlov) * (Python committer) Date: 2019-04-19 08:42
In asyncio `await asyncio.sleep(0)` is for switching execution context from the current task to other code. 
There is no guarantee for finishing already running tasks before returning from `asyncio.sleep(0)` call etc.

Also, your code snippet has a logical error. It should not call regular `time.sleep(123)` from a coroutine but use `loop.run_in_executor()` for performing blocking calls
Date User Action Args
2019-04-19 08:42:09asvetlovsetmessages: + msg340530
2019-03-14 18:43:05zach.waresetnosy: + yselivanov, asvetlov
components: + asyncio
2019-03-14 16:59:52Assaf Dayancreate