From adef0ea1e768f99fd1131aeed941cde6c695e978 Mon Sep 17 00:00:00 2001 From: Deluan Date: Tue, 15 Jul 2025 12:54:09 -0400 Subject: [PATCH] fix(plugins): resolve race condition in plugin manager registration Fixed a race condition in the plugin manager where goroutines started during plugin registration could concurrently access shared plugin maps while the main registration loop was still running. The fix separates plugin registration from background processing by collecting all plugins first, then starting background goroutines after registration is complete. This prevents concurrent read/write access to the plugins and adapters maps that was causing data races detected by the Go race detector. The solution maintains the same functionality while ensuring thread safety during the plugin scanning and registration process. Signed-off-by: Deluan --- plugins/manager.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/plugins/manager.go b/plugins/manager.go index 0800d274..7c735c74 100644 --- a/plugins/manager.go +++ b/plugins/manager.go @@ -189,13 +189,6 @@ func (m *managerImpl) registerPlugin(pluginID, pluginDir, wasmPath string, manif } m.mu.Unlock() - // Start pre-compilation of WASM module in background AFTER registration - go func() { - precompilePlugin(p) - // Check if this plugin implements InitService and hasn't been initialized yet - m.initializePluginIfNeeded(p) - }() - log.Info("Discovered plugin", "folder", pluginID, "name", manifest.Name, "capabilities", manifest.Capabilities, "wasm", wasmPath, "dev_mode", isSymlink) return m.plugins[pluginID] } @@ -261,6 +254,7 @@ func (m *managerImpl) ScanPlugins() { discoveries := DiscoverPlugins(root) var validPluginNames []string + var registeredPlugins []*plugin for _, discovery := range discoveries { if discovery.Error != nil { // Handle global errors (like directory read failure) @@ -284,7 +278,20 @@ func (m *managerImpl) ScanPlugins() { validPluginNames = append(validPluginNames, discovery.ID) // Register the plugin - m.registerPlugin(discovery.ID, discovery.Path, discovery.WasmPath, discovery.Manifest) + plugin := m.registerPlugin(discovery.ID, discovery.Path, discovery.WasmPath, discovery.Manifest) + if plugin != nil { + registeredPlugins = append(registeredPlugins, plugin) + } + } + + // Start background processing for all registered plugins after registration is complete + // This avoids race conditions between registration and goroutines that might unregister plugins + for _, p := range registeredPlugins { + go func(plugin *plugin) { + precompilePlugin(plugin) + // Check if this plugin implements InitService and hasn't been initialized yet + m.initializePluginIfNeeded(plugin) + }(p) } log.Debug("Found valid plugins", "count", len(validPluginNames), "plugins", validPluginNames)