From 2fa5bd4d1ba649d88b2b2e6d455f9acf51297ba6 Mon Sep 17 00:00:00 2001 From: Crozzers Date: Mon, 1 Jul 2024 18:35:06 +0100 Subject: [PATCH] Make a bunch of tweaks to try to fix memory errors blocking shutdown Tweaked the order of some resources getting destroyed, did a bit more manual cleanup of WX resources in `WxApp`. Also shortened the loop sleep time in SnapshotService so that it can react quicker to kill signals. --- src/gui/systray.py | 2 +- src/gui/wx_app.py | 15 +++++++++++++++ src/main.py | 11 +++++++---- src/snapshot.py | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/gui/systray.py b/src/gui/systray.py index c94e02d..30733c1 100644 --- a/src/gui/systray.py +++ b/src/gui/systray.py @@ -39,9 +39,9 @@ def CreatePopupMenu(self): return menu def exit(self): + self.RemoveIcon() if callable(self._on_exit): self._on_exit() - self.RemoveIcon() def __enter__(self): return self diff --git a/src/gui/wx_app.py b/src/gui/wx_app.py index b265bbf..f2cf287 100644 --- a/src/gui/wx_app.py +++ b/src/gui/wx_app.py @@ -20,6 +20,7 @@ class WxApp(wx.App): def __init__(self): self._log = logging.getLogger(__name__).getChild(self.__class__.__name__ + '.' + str(id(self))) super().__init__() + self.SetExitOnFrameDelete(False) def __new__(cls, *args, **kwargs): if not isinstance(getattr(cls, '_WxApp__instance', None), cls): @@ -50,6 +51,8 @@ def check_parent_alive(): if not parent or parent.is_running(): return self._log.info('parent process no longer running. exiting mainloop...') + if self.timer.IsRunning(): + self.timer.Stop() self.ExitMainLoop() # enable sigterm by regularly returning control back to python @@ -57,6 +60,18 @@ def check_parent_alive(): self._top_frame.Bind(wx.EVT_TIMER, lambda *_: check_parent_alive(), self.timer) self.timer.Start(1000) + def Destroy(self): + if self.timer.IsRunning(): + self.timer.Stop() + self.timer.Destroy() + if self._top_frame: + self._top_frame.Destroy() + if top := self.GetTopWindow(): + top.Destroy() + if top := self.GetMainTopWindow(): + top.Destroy() + return super().Destroy() + def spawn_gui(snapshot: SnapshotFile, start_page: Literal['rules', 'settings'] = 'rules'): top = WxApp()._top_frame diff --git a/src/main.py b/src/main.py index 8e3b4ab..f563197 100644 --- a/src/main.py +++ b/src/main.py @@ -8,7 +8,7 @@ import psutil import win32con import win32gui - +import wx from common import Window, XandY, load_json, local_path, match, single_call from device import DeviceChangeCallback, DeviceChangeService from gui import TaskbarIcon, WxApp, about_dialog, radio_menu @@ -190,14 +190,16 @@ def find_matching_profile(window: Window) -> Optional[dict]: @single_call def shutdown(*_): log.info('begin shutdown process') - app.ExitMainLoop() monitor_thread.stop() snapshot_service.stop() - log.debug('destroy WxApp') - app.Destroy() + window_spawn_thread.stop() log.info('save snapshot before shutting down') snap.save() + log.debug('destroy WxApp') + app.ExitMainLoop() + app.Destroy() log.info('end shutdown process') + wx.Exit() if __name__ == '__main__': @@ -288,3 +290,4 @@ def shutdown(*_): log.info('app mainloop closed') shutdown() log.debug('fin') + wx.Exit() diff --git a/src/snapshot.py b/src/snapshot.py index 82653d8..704e42b 100644 --- a/src/snapshot.py +++ b/src/snapshot.py @@ -188,6 +188,6 @@ def _runner(self, snapshot: SnapshotFile): sleep_start = time.time() while time.time() - sleep_start < settings.get('snapshot_freq', 30): - time.sleep(1) + time.sleep(0.5) if self._kill_signal.is_set(): return