-
Notifications
You must be signed in to change notification settings - Fork 601
Description
When I tried running rogerbinns/python-async-bench on free-threaded Python 3.14.2 (Linux, dual-core), I found it getting stuck on the uvloop (0.22.1) tests. Below is the code to reproduce the issue and its possible output.
#!/usr/bin/env python3
import asyncio
import threading
import time
from queue import SimpleQueue
import uvloop
START_TIME = time.monotonic()
def callback(future):
future.set_result(True)
print(f"time: {time.monotonic() - START_TIME:.10f} (callback)")
def notify_all(loop, queue):
while (future := queue.get()) is not None:
loop.call_soon_threadsafe(callback, future)
async def main():
loop = asyncio.get_running_loop()
queue = SimpleQueue()
thread = threading.Thread(target=notify_all, args=[loop, queue])
thread.start()
try:
async with asyncio.timeout(60):
while True:
future = loop.create_future()
print(f"time: {time.monotonic() - START_TIME:.10f}")
try:
async with asyncio.timeout(3):
outer_future = asyncio.shield(future)
queue.put(future)
await outer_future
except TimeoutError:
print("[timeout] future:", future)
raise
except TimeoutError:
pass
finally:
queue.put(None)
await asyncio.to_thread(thread.join)
if __name__ == "__main__":
uvloop.run(main())...
time: 0.1763970610
time: 0.1764364019 (callback)
time: 0.1764616738
time: 0.1765011698 (callback)
time: 0.1765267970
time: 3.1786725279 (callback)
[timeout] future: <Future finished result=True>
This looks like a race condition. As we can see, the handle was successfully added, but was only processed by the _on_idle() method after the timeout. It is probably related to the fact that access to the _ready_len attribute is performed non-atomically from different threads (or, in a simpler case, self._ready_len = len(self._ready) from the main thread competes with self._ready_len += 1 from the worker thread). This also applies to loop.run_in_executor() and asyncio.to_thread() running on top of it, but it is much more difficult to reproduce the issue with them (due to the greater delay between threads).
Related: #408.