github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sync/gate_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sync 16 17 import ( 18 "context" 19 "runtime" 20 "sync/atomic" 21 "testing" 22 "time" 23 ) 24 25 func TestGateBasic(t *testing.T) { 26 var g Gate 27 28 if !g.Enter() { 29 t.Fatalf("Enter failed before Close") 30 } 31 g.Leave() 32 33 g.Close() 34 if g.Enter() { 35 t.Fatalf("Enter succeeded after Close") 36 } 37 } 38 39 func TestGateConcurrent(t *testing.T) { 40 // Each call to testGateConcurrentOnce tests behavior around a single call 41 // to Gate.Close, so run many short tests to increase the probability of 42 // flushing out any issues. 43 totalTime := 5 * time.Second 44 timePerTest := 20 * time.Millisecond 45 numTests := int(totalTime / timePerTest) 46 for i := 0; i < numTests; i++ { 47 testGateConcurrentOnce(t, timePerTest) 48 } 49 } 50 51 func testGateConcurrentOnce(t *testing.T, d time.Duration) { 52 const numGoroutines = 1000 53 54 ctx, cancel := context.WithCancel(context.Background()) 55 var wg WaitGroup 56 defer func() { 57 cancel() 58 wg.Wait() 59 }() 60 61 var g Gate 62 closeState := int32(0) // set to 1 before g.Close() and 2 after it returns 63 64 // Start a large number of goroutines that repeatedly attempt to enter the 65 // gate and get the expected result. 66 for i := 0; i < numGoroutines; i++ { 67 wg.Add(1) 68 go func() { 69 defer wg.Done() 70 for ctx.Err() == nil { 71 closedBeforeEnter := atomic.LoadInt32(&closeState) == 2 72 if g.Enter() { 73 closedBeforeLeave := atomic.LoadInt32(&closeState) == 2 74 g.Leave() 75 if closedBeforeEnter { 76 t.Errorf("Enter succeeded after Close") 77 return 78 } 79 if closedBeforeLeave { 80 t.Errorf("Close returned before Leave") 81 return 82 } 83 } else { 84 if atomic.LoadInt32(&closeState) == 0 { 85 t.Errorf("Enter failed before Close") 86 return 87 } 88 } 89 // Go does not preempt busy loops until Go 1.14. 90 runtime.Gosched() 91 } 92 }() 93 } 94 95 // Allow goroutines to enter the gate successfully for half of the test's 96 // duration, then close the gate and allow goroutines to fail to enter the 97 // gate for the remaining half. 98 time.Sleep(d / 2) 99 atomic.StoreInt32(&closeState, 1) 100 g.Close() 101 atomic.StoreInt32(&closeState, 2) 102 time.Sleep(d / 2) 103 } 104 105 func BenchmarkGateEnterLeave(b *testing.B) { 106 var g Gate 107 for i := 0; i < b.N; i++ { 108 g.Enter() 109 g.Leave() 110 } 111 } 112 113 func BenchmarkGateClose(b *testing.B) { 114 for i := 0; i < b.N; i++ { 115 var g Gate 116 g.Close() 117 } 118 } 119 120 func BenchmarkGateEnterLeaveAsyncClose(b *testing.B) { 121 for i := 0; i < b.N; i++ { 122 var g Gate 123 g.Enter() 124 go func() { 125 g.Leave() 126 }() 127 g.Close() 128 } 129 }