MCPcopy
hub / github.com/redis/go-redis / TestDoubleFreeTurnSimple

Function TestDoubleFreeTurnSimple

internal/pool/double_freeturn_simple_test.go:27–157  ·  view source on GitHub ↗

TestDoubleFreeTurnSimple tests the double-free bug with a simple scenario. This test FAILS with the OLD code and PASSES with the NEW code. Scenario: 1. Request A times out, Dial A completes and delivers connection to Request B 2. Request B's own Dial B completes later 3. With the bug: Dial B frees

(t *testing.T)

Source from the content-addressed store, hash-verified

25// - With the bug: All succeed (pool size violated)
26// - With the fix: Only PoolSize succeed
27func TestDoubleFreeTurnSimple(t *testing.T) {
28 ctx := context.Background()
29
30 var dialCount atomic.Int32
31 dialBComplete := make(chan struct{})
32 requestBGotConn := make(chan struct{})
33 requestBCalledPut := make(chan struct{})
34
35 controlledDialer := func(ctx context.Context) (net.Conn, error) {
36 count := dialCount.Add(1)
37
38 if count == 1 {
39 // Dial A: takes 150ms
40 time.Sleep(150 * time.Millisecond)
41 t.Logf("Dial A completed")
42 } else if count == 2 {
43 // Dial B: takes 300ms (longer than Dial A)
44 time.Sleep(300 * time.Millisecond)
45 t.Logf("Dial B completed")
46 close(dialBComplete)
47 } else {
48 // Other dials: fast
49 time.Sleep(10 * time.Millisecond)
50 }
51
52 return newDummyConn(), nil
53 }
54
55 testPool := pool.NewConnPool(&pool.Options{
56 Dialer: controlledDialer,
57 PoolSize: 2, // Only 2 concurrent operations allowed
58 MaxConcurrentDials: 5,
59 DialTimeout: 1 * time.Second,
60 PoolTimeout: 1 * time.Second,
61 })
62 defer testPool.Close()
63
64 // Request A: Short timeout (100ms), will timeout before dial completes (150ms)
65 go func() {
66 shortCtx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
67 defer cancel()
68
69 _, err := testPool.Get(shortCtx)
70 if err != nil {
71 t.Logf("Request A: Timed out as expected: %v", err)
72 }
73 }()
74
75 // Wait for Request A to start
76 time.Sleep(20 * time.Millisecond)
77
78 // Request B: Long timeout, will receive connection from Request A's dial
79 requestBDone := make(chan struct{})
80 go func() {
81 defer close(requestBDone)
82
83 longCtx, cancel := context.WithTimeout(ctx, 1*time.Second)
84 defer cancel()

Callers

nothing calls this directly

Calls 8

CloseMethod · 0.95
GetMethod · 0.95
PutMethod · 0.95
QueueLenMethod · 0.95
NewConnPoolFunction · 0.92
newDummyConnFunction · 0.85
WithTimeoutMethod · 0.80
AddMethod · 0.65

Tested by

no test coverage detected