返回 DeepSeek-Reasonix
host_concurrency_test.go
根目录 / internal / plugin / host_concurrency_test.go
1 package plugin
2
3 import (
4 "errors"
5 "fmt"
6 "sync"
7 "testing"
8 )
9
10 // TestHostConcurrentAccess hammers the Host's mutable state from many goroutines:
11 // writers churn the failures records while readers snapshot status. The mutex
12 // must keep every read internally consistent; before it, a concurrent slice
13 // append against a copy could tear the slice header and panic.
14 func TestHostConcurrentAccess(t *testing.T) {
15 h := &Host{}
16 // Seed a few "connected" servers so the read paths have data to walk. These
17 // methods only read name/transport/toolCount, never the (nil) transport.
18 for i := 0; i < 4; i++ {
19 h.clients = append(h.clients, &Client{name: fmt.Sprintf("srv-%d", i), transport: "stdio", toolCount: i})
20 h.prompts = append(h.prompts, Prompt{Server: fmt.Sprintf("srv-%d", i), Name: "p"})
21 }
22
23 const workers = 24
24 var wg sync.WaitGroup
25 wg.Add(workers)
26 for w := 0; w < workers; w++ {
27 go func(w int) {
28 defer wg.Done()
29 for i := 0; i < 500; i++ {
30 switch (w + i) % 6 {
31 case 0:
32 h.RecordFailure(Spec{Name: fmt.Sprintf("bad-%d", i%8), Type: "stdio"}, errors.New("boom"))
33 case 1:
34 _ = h.Failures()
35 case 2:
36 _ = h.Servers()
37 case 3:
38 _ = h.ServerNames()
39 case 4:
40 _ = h.has(fmt.Sprintf("srv-%d", i%4))
41 case 5:
42 _ = h.Prompts()
43 }
44 }
45 }(w)
46 }
47 wg.Wait()
48 }
49
49 lines GO