ALL

Solo para leer y verificar. No puedes copiar, usar, modificar ni redistribuir este código. Aviso completo en la página principal.

tunnel/pump_buffers_test.go

SHA-256: ca8736dfcfd495ed7f4d8b82c9b978735ea829a31bd6e73505e8b093083dd27e
98 líneas · 2943 bytes · Ver en GitHub

1package main
2
3import (
4 "net"
5 "sync/atomic"
6 "testing"
7 "time"
8)
9
10// An idle stream spends its life blocked in a read. The pump waits with a 4 KiB buffer and takes a 32 KiB one only for a read that filled the small one, giving it back when a
11// read comes back short: an idle stream holds about 8 KiB of buffer, not 64 KiB (the chaos suite's memory-per-stream check).
12func TestPump_AnIdleStreamHoldsNoBigBufferAndABulkTransferGetsOneAndGivesItBack(t *testing.T) {
13 e := newTestEnv(t, nil)
14 srcA, srcB := tcpPair(t)
15 dstA, dstB := tcpPair(t)
16 var ctr, total atomic.Int64
17 done := make(chan error, 1)
18 go func() { done <- e.srv.pump("s1", "up", dstA, srcB, &ctr, &total, nil, nil) }()
19 drain := make(chan int, 1)
20 go func() {
21 n := 0
22 buf := make([]byte, 1<<16)
23 for {
24 k, err := dstB.Read(buf)
25 n += k
26 if err != nil {
27 drain <- n
28 return
29 }
30 }
31 }()
32 time.Sleep(100 * time.Millisecond)
33 if got := e.srv.bigBuffersInUse.Load(); got != 0 {
34 t.Fatalf("an idle pump holds %d big buffers", got)
35 }
36 // a trickle never takes one
37 for i := 0; i < 5; i++ {
38 _, _ = srcA.Write([]byte("tiny"))
39 time.Sleep(20 * time.Millisecond)
40 }
41 if got := e.srv.bigBuffersInUse.Load(); got != 0 {
42 t.Fatalf("a trickle took %d big buffers", got)
43 }
44 // a bulk write fills the small buffer: the pump takes the big one for the following reads, and gives it back when a read comes back short
45 bulk := make([]byte, 1<<20)
46 var peak atomic.Int64
47 stopPoll := make(chan struct{})
48 go func() {
49 for {
50 select {
51 case <-stopPoll:
52 return
53 default:
54 if v := e.srv.bigBuffersInUse.Load(); v > peak.Load() {
55 peak.Store(v)
56 }
57 time.Sleep(100 * time.Microsecond)
58 }
59 }
60 }()
61 go func() { _, _ = srcA.Write(bulk) }()
62 eventually(t, "the bulk transfer went through", 5*time.Second, func() bool { return ctr.Load() >= int64(len(bulk)+20) })
63 close(stopPoll)
64 if peak.Load() < 1 {
65 t.Fatalf("a bulk transfer never took a big buffer: its throughput would be that of 4 KiB reads")
66 }
67 _, _ = srcA.Write([]byte("short"))
68 eventually(t, "the big buffer went back to the pool after a short read", 3*time.Second, func() bool { return e.srv.bigBuffersInUse.Load() == 0 })
69 _ = srcA.Close()
70 if err := <-done; err != nil {
71 t.Fatalf("a clean end of the source is not an error: %v", err)
72 }
73 _ = dstA.Close()
74 if n := <-drain; n != len(bulk)+20+5 {
75 t.Fatalf("the far side received %d bytes, want %d: the byte accounting changed", n, len(bulk)+25)
76 }
77 if got := e.srv.bigBuffersInUse.Load(); got != 0 {
78 t.Fatalf("%d big buffers leaked", got)
79 }
80}
81
82func tcpPair(t *testing.T) (a, b net.Conn) {
83 t.Helper()
84 ln, err := net.Listen("tcp", "127.0.0.1:0")
85 if err != nil {
86 t.Fatal(err)
87 }
88 defer ln.Close()
89 acc := make(chan net.Conn, 1)
90 go func() { c, _ := ln.Accept(); acc <- c }()
91 a, err = net.Dial("tcp", ln.Addr().String())
92 if err != nil {
93 t.Fatal(err)
94 }
95 b = <-acc
96 t.Cleanup(func() { _ = a.Close(); _ = b.Close() })
97 return a, b
98}