1
0

run_electrum: have daemon manage Plugins object, and call Plugins.stop

Plugins.stop was never called, so the Plugins thread only stopped
because of the is_running() check in run(), which triggers too late:
the Plugins thread was stopping after the main thread stopped.

E.g. playing around in the qt wizard with wallet creation for a Trezor,
and closing the wizard (only window):
``` 24.85 | E | p/plugin.Plugins |
Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/util.py", line 386, in run_jobs
    job.run()
  File "/home/user/wspace/electrum/electrum/plugin.py", line 430, in run
    client.timeout(cutoff)
  File "/home/user/wspace/electrum/electrum/plugin.py", line 363, in wrapper
    return run_in_hwd_thread(partial(func, *args, **kwargs))
  File "/home/user/wspace/electrum/electrum/plugin.py", line 355, in run_in_hwd_thread
    fut = _hwd_comms_executor.submit(func)
  File "/usr/lib/python3.10/concurrent/futures/thread.py", line 167, in submit
    raise RuntimeError('cannot schedule new futures after shutdown')
RuntimeError: cannot schedule new futures after shutdown
```
This commit is contained in:
SomberNight
2023-08-24 16:56:23 +00:00
parent c12d9b14da
commit 90f39bce88
3 changed files with 25 additions and 8 deletions

View File

@@ -411,6 +411,7 @@ class Daemon(Logger):
if 'wallet_path' in config.cmdline_options:
self.logger.warning("Ignoring parameter 'wallet_path' for daemon. "
"Use the load_wallet command instead.")
self._plugins = None # type: Optional[Plugins]
self.asyncio_loop = util.get_asyncio_loop()
if not self.config.NETWORK_OFFLINE:
self.network = Network(config, daemon=self)
@@ -560,6 +561,9 @@ class Daemon(Logger):
return True
def run_daemon(self):
# init plugins
self._plugins = Plugins(self.config, 'cmdline')
# block until we are stopping
try:
self._stopping_soon_or_errored.wait()
except KeyboardInterrupt:
@@ -590,6 +594,11 @@ class Daemon(Logger):
if self.network:
await group.spawn(self.network.stop(full_shutdown=True))
await group.spawn(self.taskgroup.cancel_remaining())
if self._plugins:
self.logger.info("stopping plugins")
self._plugins.stop()
async with ignore_after(1):
await self._plugins.stopped_event_async.wait()
finally:
if self.listen_jsonrpc:
self.logger.info("removing lockfile")
@@ -597,18 +606,21 @@ class Daemon(Logger):
self.logger.info("stopped")
self._stopped_event.set()
def run_gui(self, config: 'SimpleConfig', plugins: 'Plugins'):
def run_gui(self) -> None:
assert self.config
assert self._plugins
threading.current_thread().name = 'GUI'
gui_name = config.GUI_NAME
gui_name = self.config.GUI_NAME
if gui_name in ['lite', 'classic']:
gui_name = 'qt'
self._plugins = Plugins(self.config, gui_name) # init plugins
self.logger.info(f'launching GUI: {gui_name}')
try:
try:
gui = __import__('electrum.gui.' + gui_name, fromlist=['electrum'])
except GuiImportError as e:
sys.exit(str(e))
self.gui_object = gui.ElectrumGui(config=config, daemon=self, plugins=plugins)
self.gui_object = gui.ElectrumGui(config=self.config, daemon=self, plugins=self._plugins)
if not self._stop_entered:
self.gui_object.main()
else: