From 08673d3534bee98e039eb6f232f666073838c58b Mon Sep 17 00:00:00 2001 From: f321x Date: Tue, 28 Oct 2025 12:01:48 +0100 Subject: [PATCH] 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 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 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`. --- electrum/util.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/electrum/util.py b/electrum/util.py index cc2733637..08e60e565 100644 --- a/electrum/util.py +++ b/electrum/util.py @@ -52,7 +52,7 @@ import functools from functools import partial from abc import abstractmethod, ABC import enum -from contextlib import nullcontext +from contextlib import nullcontext, suppress import traceback import inspect @@ -1704,8 +1704,20 @@ def create_and_start_event_loop() -> Tuple[asyncio.AbstractEventLoop, loop.run_until_complete(stopping_fut) finally: # 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 _asyncio_event_loop = None + loop.close() loop.set_exception_handler(on_exception) _set_custom_task_factory(loop)