daemon: rework stopping
The CLI stop() command can now also stop the GUI.
This commit is contained in:
@@ -217,7 +217,6 @@ class Commands:
|
||||
@command('n')
|
||||
async def stop(self):
|
||||
"""Stop daemon"""
|
||||
# TODO it would be nice if this could stop the GUI too
|
||||
await self.daemon.stop()
|
||||
return "Daemon stopped"
|
||||
|
||||
|
||||
@@ -487,8 +487,8 @@ class Daemon(Logger):
|
||||
if self.config.get('use_gossip', False):
|
||||
self.network.start_gossip()
|
||||
|
||||
self.stopping_soon = threading.Event()
|
||||
self.stopped_event = asyncio.Event()
|
||||
self._stopping_soon = threading.Event()
|
||||
self._stopped_event = threading.Event()
|
||||
self.taskgroup = TaskGroup()
|
||||
asyncio.run_coroutine_threadsafe(self._run(jobs=daemon_jobs), self.asyncio_loop)
|
||||
|
||||
@@ -507,7 +507,7 @@ class Daemon(Logger):
|
||||
self.logger.exception("taskgroup died.")
|
||||
finally:
|
||||
self.logger.info("taskgroup stopped.")
|
||||
self.stopping_soon.set()
|
||||
await self.stop()
|
||||
|
||||
def load_wallet(self, path, password, *, manual_upgrades=True) -> Optional[Abstract_Wallet]:
|
||||
path = standardize_path(path)
|
||||
@@ -571,41 +571,35 @@ class Daemon(Logger):
|
||||
|
||||
def run_daemon(self):
|
||||
try:
|
||||
self.stopping_soon.wait()
|
||||
self._stopping_soon.wait()
|
||||
except KeyboardInterrupt:
|
||||
self.stopping_soon.set()
|
||||
self.on_stop()
|
||||
asyncio.run_coroutine_threadsafe(self.stop(), self.asyncio_loop).result()
|
||||
self._stopped_event.wait()
|
||||
|
||||
async def stop(self):
|
||||
self.stopping_soon.set()
|
||||
await self.stopped_event.wait()
|
||||
|
||||
def on_stop(self):
|
||||
if self._stopping_soon.is_set():
|
||||
return
|
||||
self._stopping_soon.set()
|
||||
self.logger.info("stop() entered. initiating shutdown")
|
||||
try:
|
||||
self.logger.info("on_stop() entered. initiating shutdown")
|
||||
if self.gui_object:
|
||||
self.gui_object.stop()
|
||||
|
||||
async def stop_async():
|
||||
self.logger.info("stopping all wallets")
|
||||
self.logger.info("stopping all wallets")
|
||||
async with TaskGroup() as group:
|
||||
for k, wallet in self._wallets.items():
|
||||
await group.spawn(wallet.stop())
|
||||
self.logger.info("stopping network and taskgroup")
|
||||
async with ignore_after(2):
|
||||
async with TaskGroup() as group:
|
||||
for k, wallet in self._wallets.items():
|
||||
await group.spawn(wallet.stop())
|
||||
self.logger.info("stopping network and taskgroup")
|
||||
async with ignore_after(2):
|
||||
async with TaskGroup() as group:
|
||||
if self.network:
|
||||
await group.spawn(self.network.stop(full_shutdown=True))
|
||||
await group.spawn(self.taskgroup.cancel_remaining())
|
||||
|
||||
fut = asyncio.run_coroutine_threadsafe(stop_async(), self.asyncio_loop)
|
||||
fut.result()
|
||||
if self.network:
|
||||
await group.spawn(self.network.stop(full_shutdown=True))
|
||||
await group.spawn(self.taskgroup.cancel_remaining())
|
||||
finally:
|
||||
if self.listen_jsonrpc:
|
||||
self.logger.info("removing lockfile")
|
||||
remove_lockfile(get_lockfile(self.config))
|
||||
self.logger.info("stopped")
|
||||
self.asyncio_loop.call_soon_threadsafe(self.stopped_event.set)
|
||||
self._stopped_event.set()
|
||||
|
||||
def run_gui(self, config, plugins):
|
||||
threading.current_thread().setName('GUI')
|
||||
@@ -616,10 +610,14 @@ class Daemon(Logger):
|
||||
try:
|
||||
gui = __import__('electrum.gui.' + gui_name, fromlist=['electrum'])
|
||||
self.gui_object = gui.ElectrumGui(config, self, plugins)
|
||||
self.gui_object.main()
|
||||
if not self._stopping_soon.is_set():
|
||||
self.gui_object.main()
|
||||
else:
|
||||
# If daemon.stop() was called before gui_object got created, stop gui now.
|
||||
self.gui_object.stop()
|
||||
except BaseException as e:
|
||||
self.logger.error(f'GUI raised exception: {repr(e)}. shutting down.')
|
||||
raise
|
||||
finally:
|
||||
# app will exit now
|
||||
self.on_stop()
|
||||
asyncio.run_coroutine_threadsafe(self.stop(), self.asyncio_loop).result()
|
||||
|
||||
@@ -81,6 +81,7 @@ class OpenFileEventFilter(QObject):
|
||||
|
||||
class QElectrumApplication(QApplication):
|
||||
new_window_signal = pyqtSignal(str, object)
|
||||
quit_signal = pyqtSignal()
|
||||
|
||||
|
||||
class QNetworkUpdatedSignalObject(QObject):
|
||||
@@ -132,6 +133,7 @@ class ElectrumGui(Logger):
|
||||
self.tray = None
|
||||
self._init_tray()
|
||||
self.app.new_window_signal.connect(self.start_new_window)
|
||||
self.app.quit_signal.connect(self.app.quit, Qt.QueuedConnection)
|
||||
self.set_dark_theme_if_needed()
|
||||
run_hook('init_qt', self)
|
||||
|
||||
@@ -428,5 +430,8 @@ class ElectrumGui(Logger):
|
||||
# on some platforms the exec_ call may not return, so use _cleanup_before_exit
|
||||
|
||||
def stop(self):
|
||||
"""Stops the GUI.
|
||||
This method is thread-safe.
|
||||
"""
|
||||
self.logger.info('closing GUI')
|
||||
self.app.quit()
|
||||
self.app.quit_signal.emit()
|
||||
|
||||
Reference in New Issue
Block a user