From b5d8d31723720de1c415a3cce7f3378b7b77d181 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Sun, 17 Dec 2023 06:07:37 +0000 Subject: [PATCH] plugins: follow-up prev, log thread name when loading plugins Correction to comment in prev commit (and removing it here): spec.loader.exec_module does not spawn new threads, it simply executes the module in the current thread. I got confused but turns out "load_plugin" itself is sometimes not called from the main thread. Specifically (e.g.), since the recent wizard rewrite, in the qt gui, the wizard loads the hww plugins from a new thread. This now better explains the macos hww crashes: they had started appearing because we upgraded hidapi (which made it more sensitive to having to import from main thread) AND scanning(->importing) from the wizard no longer happened on the main thread after the rewrite. Plugins should be thread-safe in terms of where they are imported from. Let's log the importer thread's name (added here), to help recognise related threading issues. --- electrum/plugin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/electrum/plugin.py b/electrum/plugin.py index 3ea17c3c1..aad9bb896 100644 --- a/electrum/plugin.py +++ b/electrum/plugin.py @@ -131,6 +131,9 @@ class Plugins(DaemonThread): return len(self.plugins) def load_plugin(self, name) -> 'BasePlugin': + """Imports the code of the given plugin. + note: can be called from any thread. + """ if name in self.plugins: return self.plugins[name] full_name = f'electrum.plugins.{name}.{self.gui_name}' @@ -140,13 +143,13 @@ class Plugins(DaemonThread): % (self.gui_name, name)) try: module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) # note: imports the plugin code in a *different* thread + spec.loader.exec_module(module) plugin = module.Plugin(self, self.config, name) except Exception as e: raise Exception(f"Error loading {name} plugin: {repr(e)}") from e self.add_jobs(plugin.thread_jobs()) self.plugins[name] = plugin - self.logger.info(f"loaded {name}") + self.logger.info(f"loaded plugin {name!r}. (from thread: {threading.current_thread().name!r})") return plugin def close_plugin(self, plugin):