github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/sync/worker_pool_test.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package sync 22 23 import ( 24 stdctx "context" 25 "sync" 26 "sync/atomic" 27 "testing" 28 "time" 29 30 "github.com/stretchr/testify/require" 31 32 "github.com/m3db/m3/src/x/context" 33 ) 34 35 const testWorkerPoolSize = 5 36 37 func TestGo(t *testing.T) { 38 var count uint32 39 40 p := NewWorkerPool(testWorkerPoolSize) 41 p.Init() 42 43 var wg sync.WaitGroup 44 for i := 0; i < testWorkerPoolSize*2; i++ { 45 wg.Add(1) 46 p.Go(func() { 47 atomic.AddUint32(&count, 1) 48 wg.Done() 49 }) 50 } 51 wg.Wait() 52 53 require.Equal(t, uint32(testWorkerPoolSize*2), count) 54 } 55 56 func TestGoIfAvailable(t *testing.T) { 57 var count uint32 58 59 p := NewWorkerPool(testWorkerPoolSize) 60 p.Init() 61 62 start := make(chan struct{}) 63 64 var wg sync.WaitGroup 65 for i := 0; i < testWorkerPoolSize; i++ { 66 wg.Add(1) 67 p.Go(func() { 68 <-start 69 atomic.AddUint32(&count, 1) 70 wg.Done() 71 }) 72 } 73 74 // Should fail since no workers are available 75 require.False(t, p.GoIfAvailable(func() {})) 76 77 // Release blocked goroutines and wait for completion 78 close(start) 79 wg.Wait() 80 81 // Spin up another, should execute since there is a worker available 82 wg.Add(1) 83 p.GoIfAvailable(func() { 84 atomic.AddUint32(&count, 1) 85 wg.Done() 86 }) 87 wg.Wait() 88 89 require.Equal(t, uint32(testWorkerPoolSize+1), count) 90 } 91 92 func TestGoWithTimeout(t *testing.T) { 93 var count uint32 94 95 p := NewWorkerPool(testWorkerPoolSize) 96 p.Init() 97 98 start := make(chan struct{}) 99 100 var wg sync.WaitGroup 101 for i := 0; i < testWorkerPoolSize; i++ { 102 wg.Add(1) 103 p.Go(func() { 104 <-start 105 atomic.AddUint32(&count, 1) 106 wg.Done() 107 }) 108 } 109 110 // Should timeout 111 require.False(t, p.GoWithTimeout(func() {}, time.Millisecond*500)) 112 113 // Release blocked goroutines and wait for completion 114 close(start) 115 wg.Wait() 116 117 // Spin up another, should execute since there is a worker available 118 wg.Add(1) 119 p.GoWithTimeout(func() { 120 atomic.AddUint32(&count, 1) 121 wg.Done() 122 }, time.Second*5) 123 wg.Wait() 124 125 require.Equal(t, uint32(testWorkerPoolSize+1), count) 126 } 127 128 func TestGoWithContext(t *testing.T) { 129 sleep := time.Second 130 goctx, cancel := stdctx.WithTimeout(stdctx.Background(), sleep) 131 defer cancel() 132 ctx := context.NewWithGoContext(goctx) 133 wp := NewWorkerPool(1) 134 wp.Init() 135 136 result := wp.GoWithContext(ctx, func() { 137 time.Sleep(5 * sleep) 138 }) 139 require.True(t, result.Available) 140 141 result = wp.GoWithContext(ctx, func() {}) 142 require.False(t, result.Available) 143 } 144 145 func TestFast(t *testing.T) { 146 wp := NewWorkerPool(1) 147 wp.Init() 148 149 fast := wp.FastContextCheck(3) 150 151 goctx, cancel := stdctx.WithCancel(stdctx.Background()) 152 cancel() 153 ctx := context.NewWithGoContext(goctx) 154 155 require.False(t, fast.GoWithContext(ctx, func() {}).Available) 156 require.True(t, fast.GoWithContext(ctx, func() {}).Available) 157 require.True(t, fast.GoWithContext(ctx, func() {}).Available) 158 require.False(t, fast.GoWithContext(ctx, func() {}).Available) 159 require.True(t, fast.GoWithContext(ctx, func() {}).Available) 160 }