-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ctrl-c / KeyboardInterrupt makes event loop exceptions #8
Comments
Thanks for reporting, I'll look into this! |
I haven't found a complete solution yet, but the problem appears to be that |
Fixes #8. This PR changes the behaviour of `asyncio.greenlet_to_future` by making sure that the greenlet is awaited in a separate thread which prevents blocking of the main thread and makes interrupts safe to use with the code. The PR also adds a note to the README describing how to work with `gevent.sleep`.
I've found the reason for this behaviour and implemented a fix in #10. The change has been released as version The issue is that the The solution is to run any code that runs Example: import gevent.monkey
gevent.monkey.patch_all()
import asyncio
import threading
import gevent
import asyncio_gevent
asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())
async def f():
print("f", 1)
await asyncio.sleep(1)
print("f", 2)
def g():
print("g", 1)
gevent.sleep(2)
print("g", 2)
async def main():
await asyncio.gather(f(), asyncio_gevent.sync_to_async(g)())
# OR equivalently
# await asyncio.gather(f(), asyncio_gevent.greenlet_to_future(gevent.spawn(g)))
if __name__ == "__main__":
asyncio.run(main()) The output will be (as expected): g 1
f 1
f 2
g 2 If import gevent.monkey
gevent.monkey.patch_all()
import asyncio
import gevent
import asyncio_gevent
asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())
async def f():
print("f", 1)
await asyncio.sleep(1)
print("f", 2)
async def g():
print("g", 1)
await asyncio.sleep(1)
gevent.sleep(1)
print("g", 2)
async def main():
await asyncio.gather(
f(), asyncio_gevent.sync_to_async(asyncio_gevent.async_to_sync(g))()
)
if __name__ == "__main__":
asyncio.run(main()) The output will again be (as expected): g 1
f 1
f 2
g 2 In both cases, you can I hope this fixes your issue as well. Please re-open the ticket if you still encounter this issue. |
I have a suggestion to optmize this workaround. Right now we use default executor (None) to join greenlet, and this can produce a lot of threads or can get deadlock if no more threads can be spawned to join greenlet (context switch) wait_executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
async def _await_greenlet(
...
result, _ = await asyncio.gather(
future, loop.run_in_executor(wait_executor, gevent.sleep, 0) # or we can use gevent.idle
) I was testing it with |
Can i make a PR? :) |
Sure, go ahead :) |
Exiting
asyncio.run(<a main function()>)
with SIGINT (ctrl-c, KeyboardInterrupt exception) makes badRuntimeError
exceptions. I am affected by this in a prompt-toolkit application relying on gevent.The minimum code to reproduce the problem:
The text was updated successfully, but these errors were encountered: