fix(ipc): a plugin reload no longer freezes the panel during Vegas (#723)

A plugin.reload no longer freezes the panel during Vegas: the old instance is torn down and the new one loaded off the render thread (frame gap 3017 ms -> 9 ms in the ledpi reproduction). A failed or timed-out teardown stops the reload with a restart hint instead of loading over stale modules or tearing down an instance still in use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-02 13:42:23 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 5aa7a63127
commit 8136a2d525
9 changed files with 905 additions and 49 deletions
+20
View File
@@ -183,9 +183,27 @@ class PluginAdapter:
"round", plugin_id, self.PLUGIN_LOCK_TIMEOUT
)
return None
if not self._still_loaded(plugin, plugin_id):
return None
return self._fetch_content(plugin, plugin_id, restricted=False,
keyed=keyed)
def _still_loaded(self, plugin: 'BasePlugin', plugin_id: str) -> bool:
"""Whether ``plugin`` is still the loaded instance of ``plugin_id``.
Checked once the plugin's lock is held: a reload or a disable can
take the instance out and tear it down while this fetch waited for
the lock (PluginManager.detach_plugin), and a torn-down instance is
not asked for content. True when the manager keeps no ``plugins``
mapping to ask.
"""
plugins = getattr(self.plugin_manager, 'plugins', None)
if not isinstance(plugins, dict) or plugins.get(plugin_id) is plugin:
return True
logger.debug("[%s] Unloaded or reloaded while waiting for its lock; "
"skipping the old instance", plugin_id)
return False
def is_live_capable(self, plugin: 'BasePlugin', plugin_id: str) -> bool:
"""Whether to ask this plugin for live elements rather than pictures.
@@ -922,6 +940,8 @@ class PluginAdapter:
return None
epochs = self.live_epochs
epoch = epochs.get(plugin_id) if epochs is not None else 0
if not self._still_loaded(plugin, plugin_id):
return epoch, {}
render_width = self.resolve_render_width(plugin, plugin_id)
plugin._vegas_render_width = render_width
try:
+5 -2
View File
@@ -333,8 +333,11 @@ class StreamManager:
loaded = 0
if hasattr(self.plugin_manager, 'plugins'):
loaded = len(self.plugin_manager.plugins)
for plugin_id, plugin in self.plugin_manager.plugins.items():
# A snapshot: a plugin reload adds and removes entries on its
# own thread (DisplayController._start_plugin_reload).
plugins = list(self.plugin_manager.plugins.items())
loaded = len(plugins)
for plugin_id, plugin in plugins:
if not getattr(plugin, 'enabled', False):
logger.debug("[%s] Vegas: skipped (not enabled)", plugin_id)
continue