1
0

util: cleanup asyncio event loop after stopping

I noticed many ResourceWarning when running regtests with
PYTHONASYNCIODEBUG=1 and PYTHONDEVMODE=1, each time a daemon
gets stopped the asyncio loop wouldn't get properly cleaned up:

```
(env) user@hp:~/code/electrum-fork$ python3 -m unittest tests.regtest.TestLightningAB.test_lnwatcher_waits_until_fees_go_down
***** test_lnwatcher_waits_until_fees_go_down ******
initializing alice
  0.67 | W | asyncio | Executing <Task finished name='Task-1' coro=<run_offline_command() done, defined at /home/user/code/electrum-fork/./run_electrum:229> result={'msg': 'Please keep ... your wallet.', 'path': '/tmp/alice/r...efault_wallet', 'seed': 'fiction sadd...it radar desk'} created at /home/user/code/electrum-fork/electrum/util.py:1760> took 0.280 seconds
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
funding alice
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
f84277454a04243e500cf84c67aad16e04dd7a88ffa849ffcf20ce3f9af277df
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
initializing bob
  0.54 | W | asyncio | Executing <Task finished name='Task-1' coro=<run_offline_command() done, defined at /home/user/code/electrum-fork/./run_electrum:229> result={'msg': 'Please keep ... your wallet.', 'path': '/tmp/bob/reg...efault_wallet', 'seed': 'wink loud so...ory myth case'} created at /home/user/code/electrum-fork/electrum/util.py:1760> took 0.195 seconds
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
funding bob
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
f68b651e84dc8547f54dd09129018a2d0d256dedc8ccc48595ae172de895371a
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
mining 1 blocks
starting daemon (PID 38153)
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
/tmp/alice/regtest/wallets/default_wallet
/usr/lib64/python3.14/asyncio/base_events.py:758: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=True>
```

This commits adds some cleanup to `util.create_and_start_event_loop()`
to
1. cancel remaining tasks
2. shut down asyncgens
3. shutdown the default executor
4. call loop.close() to free the resources allocated to the loop
See https://stackoverflow.com/questions/30765606/whats-the-correct-way-to-clean-up-after-an-interrupted-event-loop

This seems to reliably solve the mentioned `ResourceWarning`.
This commit is contained in:
f321x
2025-10-28 12:01:48 +01:00
parent 6423323037
commit 08673d3534

View File

@@ -52,7 +52,7 @@ import functools
from functools import partial from functools import partial
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
import enum import enum
from contextlib import nullcontext from contextlib import nullcontext, suppress
import traceback import traceback
import inspect import inspect
@@ -1704,8 +1704,20 @@ def create_and_start_event_loop() -> Tuple[asyncio.AbstractEventLoop,
loop.run_until_complete(stopping_fut) loop.run_until_complete(stopping_fut)
finally: finally:
# clean-up # clean-up
try:
pending_tasks = asyncio.gather(*asyncio.all_tasks(loop), return_exceptions=True)
pending_tasks.cancel()
with suppress(asyncio.CancelledError):
loop.run_until_complete(pending_tasks)
loop.run_until_complete(loop.shutdown_asyncgens())
if isinstance(loop, asyncio.BaseEventLoop):
loop.run_until_complete(loop.shutdown_default_executor())
except Exception as e:
_logger.debug(f"exception when cleaning up asyncio event loop: {e}")
global _asyncio_event_loop global _asyncio_event_loop
_asyncio_event_loop = None _asyncio_event_loop = None
loop.close()
loop.set_exception_handler(on_exception) loop.set_exception_handler(on_exception)
_set_custom_task_factory(loop) _set_custom_task_factory(loop)