You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
call_soon_threadsafe is not really threadsafe #408
Can you reproduce the bug with PYTHONASYNCIODEBUG in env?: yes
Does uvloop behave differently from vanilla asyncio? How?: yes
I have been confused about this bug for about two weeks. In my situation, I create some threads and every thread run a new loop separately. At the same time, I have another thread to invoke loop.call_soon_threadsafe to add tasks to loops in other threads. It ran well at first. However loops would close after a-few-hour running and raised RuntimeError('Event loop stopped before Future completed.'). Actually some tasks still left in the loop and I haven't closed the loop yet.
Because this problem is really hard to reproduce . I took a long time to figure out how it happened and checked if I invoked loop.close in somewhere. I added some log at close and found the loop stopped suddenly without close invocation. So I went on and checked libuv docs. There is a suspicious sentence If the loop is alive an iteration is started, otherwise the loop will exit immediately. So, when is a loop considered to be alive? If a loop has active and ref’d handles, active requests or closing handles it’s considered to be alive.. I added some log in uv.uv_run and it verified that the loop stoped because uv__loop_alive return False!
I dove into uvloop and libuv carefully. Disappointedly I found nothing. Code are rigorous and elegant. So I logged around every init and stop when uvloop invoke uv_handle_t.Just like this
After a few hours execution and 200 thousand lines log file generated, I reproduced the bug again! And the highlight line is where the problem happened.
Because libuv isn't thread safe. The uv_loop_t.active_handles will in race condition when we invoke call_soon_threadsafe in a thread and the loop runs in another thread simultaneously. Actually the loop.handle_async.send will activate the idle handle in the loop thread. So we don't need to activate the idle handle at call_soon_threadsafe
Sorry for the late reply and thank you for the careful debugging! The PR removed the start of the idle handler in call_soon_threadsafe() because starting an idle handler is not thread-safe and may potentially overtake other handles scheduled at the same time leading to an early loop exit, meanwhile the async handler will anyway start the idle handler properly from the loop thread?
- if not self.handler_idle.running:- self.handler_idle.start()
That does make sense to me. It would be great if we could have a test to cover the case though.
Activity
hehaha commentedon May 11, 2021
Can you please help me and review the PR? And if it's possible to release a new version to fix this problem? @fantix @graingert
fantix commentedon Jul 12, 2021
Sorry for the late reply and thank you for the careful debugging! The PR removed the start of the idle handler in
call_soon_threadsafe()because starting an idle handler is not thread-safe and may potentially overtake other handles scheduled at the same time leading to an early loop exit, meanwhile the async handler will anyway start the idle handler properly from the loop thread?That does make sense to me. It would be great if we could have a test to cover the case though.
hehaha commentedon Jul 13, 2021
Thanks God for you reply me finally! It's do better to have a test case.
fantix commentedon Jul 13, 2021
Fixed in uvloop 0.15.3, please feel free to reopen if the issue persists.