I have the following code snippet being run by python 3.10.5:
import time
import asyncio
async def main():
loop = asyncio.get_running_loop()
loop.run_in_executor(None, blocking)
print(f"{time.ctime()} Hello!")
await asyncio.sleep(1.0)
print(f"{time.ctime()} Goodbye!")
def blocking():
time.sleep(5.0)
print(f"{time.ctime()} Hello from thread!")
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Cancelled.")
When I let it run it exits gracefully due to shutdown_default_executor() method added in python 3.9 which allows to solve the problem of task running in executor that outlasts the main event loop by wrapping this task in the coroutine. So I have the following output:
Sun Sep 11 19:04:25 2022 Hello!
Sun Sep 11 19:04:26 2022 Goodbye!
Sun Sep 11 19:04:30 2022 Hello from thread!
Next when I am pressing Ctrl-C after the first line of output, I am getting:
Sun Sep 11 19:04:42 2022 Hello!
^CSun Sep 11 19:04:47 2022 Hello from thread!
Cancelled.
So it is still able to handle the situation. But when I do it after Goodbye! line (when main coroutine is already finished and I am waiting for task in the executor to finish) I am getting:
Sun Sep 11 19:04:49 2022 Hello!
Sun Sep 11 19:04:50 2022 Goodbye!
^CCancelled.
Sun Sep 11 19:04:54 2022 Hello from thread!
exception calling callback for <Future at 0x7f58475183d0 state=finished returned NoneType>
Traceback (most recent call last):
File "/usr/lib/python3.10/concurrent/futures/_base.py", line 330, in _invoke_callbacks
callback(self)
File "/usr/lib/python3.10/asyncio/futures.py", line 398, in _call_set_state
dest_loop.call_soon_threadsafe(_set_state, destination, source)
File "/usr/lib/python3.10/asyncio/base_events.py", line 795, in call_soon_threadsafe
self._check_closed()
File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Exception in thread Thread-1 (_do_shutdown):
Traceback (most recent call last):
File "/usr/lib/python3.10/asyncio/base_events.py", line 576, in _do_shutdown
self.call_soon_threadsafe(future.set_result, None)
File "/usr/lib/python3.10/asyncio/base_events.py", line 795, in call_soon_threadsafe
self._check_closed()
File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
self.run()
File "/usr/lib/python3.10/threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python3.10/asyncio/base_events.py", line 578, in _do_shutdown
self.call_soon_threadsafe(future.set_exception, ex)
File "/usr/lib/python3.10/asyncio/base_events.py", line 795, in call_soon_threadsafe
self._check_closed()
File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
The question is why am I getting runtime error here but manage to avoid it when I was hitting Ctrl-C after Hello! line (second case)? How do I handle this Runtime Error gracefully?