fix(multiagent): guard MCPExecutionBinder map with RWMutex

Concurrent tool callbacks in eino_adk_run_loop and the MCP monitor
goroutine call Bind/ExecutionID on the same binder. The previous
implementation used a plain map[string]string, causing intermittent
'concurrent map read and map write' panics — the same race that
TestMCPExecutionBinder_ConcurrentBind was added to guard against.

Wrap the map with sync.RWMutex (write lock in Bind, read lock in
ExecutionID). The TrimSpace of toolCallID/executionID stays outside
the critical section to keep it minimal.

go vet ./internal/multiagent/...   ✓
go test -race ./internal/multiagent/... -run MCPExecutionBinder  ✓
This commit is contained in:
flydream
2026-07-06 14:37:56 +08:00
parent 754b1ad88b
commit 49a90d03fa
+11 -2
View File
@@ -1,9 +1,13 @@
package multiagent
import "strings"
import (
"strings"
"sync"
)
// MCPExecutionBinder maps ADK toolCallID → MCP monitor execution ID for a single agent run.
type MCPExecutionBinder struct {
mu sync.RWMutex
byToolCall map[string]string
}
@@ -20,12 +24,17 @@ func (b *MCPExecutionBinder) Bind(toolCallID, executionID string) {
if tid == "" || eid == "" {
return
}
b.mu.Lock()
b.byToolCall[tid] = eid
b.mu.Unlock()
}
func (b *MCPExecutionBinder) ExecutionID(toolCallID string) string {
if b == nil {
return ""
}
return b.byToolCall[strings.TrimSpace(toolCallID)]
tid := strings.TrimSpace(toolCallID)
b.mu.RLock()
defer b.mu.RUnlock()
return b.byToolCall[tid]
}