github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xgopool/xgopool_test.go (about) 1 package xgopool 2 3 import ( 4 "context" 5 "fmt" 6 "github.com/Aoi-hosizora/ahlib/xtesting" 7 "runtime" 8 "sync" 9 "sync/atomic" 10 "testing" 11 ) 12 13 func TestSimpleGo(t *testing.T) { 14 t.Run("cap", func(t *testing.T) { 15 xtesting.Panic(t, func() { New(0) }) 16 xtesting.Panic(t, func() { New(-1) }) 17 xtesting.NotPanic(t, func() { New(1) }) 18 19 serializedPool := New(1) 20 xtesting.Equal(t, serializedPool.WorkersCap(), int32(1)) 21 xtesting.Panic(t, func() { serializedPool.SetWorkersCap(0) }) 22 xtesting.Panic(t, func() { serializedPool.SetWorkersCap(-1) }) 23 xtesting.NotPanic(t, func() { serializedPool.SetWorkersCap(2) }) 24 xtesting.Equal(t, serializedPool.WorkersCap(), int32(2)) 25 xtesting.NotPanic(t, func() { serializedPool.SetWorkersCap(100) }) 26 xtesting.Equal(t, serializedPool.WorkersCap(), int32(100)) 27 28 xtesting.Equal(t, WorkersCap(), int32(10000)) 29 SetWorkersCap(100) 30 xtesting.Equal(t, WorkersCap(), int32(100)) 31 SetWorkersCap(10000) 32 xtesting.Equal(t, WorkersCap(), int32(10000)) 33 xtesting.Equal(t, NumWorkers(), int32(0)) 34 xtesting.Equal(t, NumTasks(), int32(0)) 35 }) 36 37 t.Run("incr", func(t *testing.T) { 38 wg := sync.WaitGroup{} 39 tmp := 0 40 incr := func() { tmp++; wg.Done() } 41 42 wg.Add(1) 43 Go(incr) 44 wg.Wait() 45 xtesting.Equal(t, tmp, 1) 46 // 47 wg.Add(1) 48 Go(incr) 49 wg.Wait() 50 xtesting.Equal(t, tmp, 2) 51 // 52 numTasks := _defaultPool.numTasks 53 Go(nil) 54 xtesting.Equal(t, _defaultPool.numTasks, numTasks) 55 xtesting.Equal(t, tmp, 2) 56 // 57 wg.Add(1) 58 Go(incr) 59 wg.Wait() 60 xtesting.Equal(t, tmp, 3) 61 62 xtesting.Equal(t, NumWorkers(), int32(0)) 63 xtesting.Equal(t, NumTasks(), int32(0)) 64 }) 65 66 t.Run("decr", func(t *testing.T) { 67 wg := sync.WaitGroup{} 68 tmp := 3 69 decr := func(_ context.Context) { tmp--; wg.Done() } 70 71 wg.Add(1) 72 CtxGo(context.Background(), decr) 73 wg.Wait() 74 xtesting.Equal(t, tmp, 2) 75 // 76 wg.Add(1) 77 CtxGo(context.Background(), decr) 78 wg.Wait() 79 xtesting.Equal(t, tmp, 1) 80 // 81 numTasks := _defaultPool.numTasks 82 CtxGo(context.Background(), nil) 83 xtesting.Equal(t, _defaultPool.numTasks, numTasks) 84 xtesting.Equal(t, tmp, 1) 85 // 86 wg.Add(1) 87 CtxGo(context.Background(), decr) 88 wg.Wait() 89 xtesting.Equal(t, tmp, 0) 90 91 xtesting.Equal(t, NumWorkers(), int32(0)) 92 xtesting.Equal(t, NumTasks(), int32(0)) 93 }) 94 95 t.Run("atomic add", func(t *testing.T) { 96 wg := sync.WaitGroup{} 97 p := New(100) 98 n := int32(0) 99 for i := 0; i < 2000; i++ { 100 wg.Add(1) 101 p.Go(func() { 102 atomic.AddInt32(&n, 1) 103 wg.Done() 104 }) 105 } 106 wg.Wait() 107 xtesting.Equal(t, n, int32(2000)) 108 109 p.SetWorkersCap(2000) 110 n = 0 111 for i := 0; i < 2000; i++ { 112 wg.Add(1) 113 p.Go(func() { 114 atomic.AddInt32(&n, 1) 115 wg.Done() 116 }) 117 } 118 wg.Wait() 119 xtesting.Equal(t, n, int32(2000)) 120 }) 121 } 122 123 func TestPanicGo(t *testing.T) { 124 terminated := make(chan struct{}) 125 done := func() { terminated <- struct{}{} } 126 wait := func() { <-terminated } 127 128 t.Run("default panic handler", func(t *testing.T) { 129 originHandler := _defaultPool.panicHandler 130 SetPanicHandler(func(ctx context.Context, i interface{}) { 131 originHandler(ctx, i) 132 done() 133 }) 134 defer SetPanicHandler(originHandler) 135 Go(func() { panic("panic") }) 136 // Warning: Panic with `panic` 137 wait() 138 }) 139 140 t.Run("panic with no panic handler 1", func(t *testing.T) { 141 g := New(100) 142 g.SetPanicHandler(nil) 143 g.Go(func() { 144 defer done() 145 defer func() { err := recover(); xtesting.Equal(t, err, "panic") }() 146 panic("panic") 147 }) 148 wait() 149 }) 150 151 t.Run("panic with no panic handler 2", func(t *testing.T) { 152 g := New(100) 153 g.SetPanicHandler(nil) 154 _testFlag.Store(true) // setup test flag 155 g.Go(func() { 156 panic("panic for testing ...") 157 }) 158 // Panic when testing: `panic for testing ...` 159 for _testFlag.Load() == true { // wait for the panic handler to finish 160 } 161 }) 162 163 t.Run("panic with msg panic handler", func(t *testing.T) { 164 g := New(100) 165 msg := new(string) 166 g.SetPanicHandler(func(_ context.Context, i interface{}) { 167 *msg = fmt.Sprintf("%v_%v", i, i) 168 done() 169 }) 170 g.Go(func() { panic("panic") }) 171 wait() 172 xtesting.Equal(t, *msg, "panic_panic") 173 }) 174 175 t.Run("panic with context panic handler", func(t *testing.T) { 176 g := New(100) 177 msg := new(string) 178 g.SetPanicHandler(func(ctx context.Context, i interface{}) { 179 *msg = fmt.Sprintf("%v_%v", i, ctx.Value("id")) 180 done() 181 }) 182 g.CtxGo(context.WithValue(context.Background(), "id", 333), func(ctx context.Context) { 183 xtesting.Equal(t, ctx.Value("id"), 333) 184 panic("panic") 185 }) 186 wait() 187 xtesting.Equal(t, *msg, "panic_333") 188 }) 189 } 190 191 func TestWorkersCap(t *testing.T) { 192 const N = 5000 193 integers := new([]int) 194 matchedIntegers := make([]int, 0, N) 195 for i := 0; i < N; i++ { 196 matchedIntegers = append(matchedIntegers, i) 197 } 198 199 t.Run("serialize", func(t *testing.T) { 200 *integers = make([]int, 0, N) 201 serializedPool := New(1) 202 for i := 0; i < N; i++ { 203 i := i 204 serializedPool.Go(func() { 205 xtesting.Equal(t, serializedPool.NumWorkers(), int32(1)) 206 *integers = append(*integers, i) 207 }) 208 } 209 terminated := make(chan struct{}) 210 serializedPool.Go(func() { 211 xtesting.Equal(t, *integers, matchedIntegers) 212 close(terminated) 213 }) 214 <-terminated 215 }) 216 217 t.Run("serialized parallel", func(t *testing.T) { 218 *integers = make([]int, 0, N) 219 parallelizedPool := New(2) 220 terminated := make(chan struct{}) 221 for i := 0; i < N; i++ { 222 i := i 223 parallelizedPool.Go(func() { 224 xtesting.True(t, parallelizedPool.NumWorkers() <= 2) 225 xtesting.Equal(t, parallelizedPool.NumTasks(), int32(0)) 226 *integers = append(*integers, i) 227 terminated <- struct{}{} 228 }) 229 <-terminated 230 } 231 close(terminated) 232 xtesting.Equal(t, *integers, matchedIntegers) 233 }) 234 235 t.Run("parallel", func(t *testing.T) { 236 *integers = make([]int, 0, N) 237 parallelizedPool := New(5) 238 mu := sync.Mutex{} 239 wg := sync.WaitGroup{} 240 for i := 0; i < N; i++ { 241 i := i 242 wg.Add(1) 243 parallelizedPool.Go(func() { 244 mu.Lock() 245 *integers = append(*integers, i) 246 mu.Unlock() 247 wg.Done() 248 }) 249 } 250 wg.Wait() 251 xtesting.ElementMatch(t, *integers, matchedIntegers) 252 }) 253 } 254 255 // benchmarks are referred from https://github.com/bytedance/gopkg/blob/develop/util/gopool/pool_test.go. 256 257 const benchmarkTimes = 20000 258 259 func doCopyStack(_, b int) int { 260 if b < 100 { 261 return doCopyStack(0, b+1) 262 } 263 return 0 264 } 265 266 func testFunc() { 267 doCopyStack(0, 0) 268 } 269 270 func BenchmarkPool(b *testing.B) { 271 b.Run("GoPool_xgopool", func(b *testing.B) { 272 p := New(int32(runtime.GOMAXPROCS(0))) 273 var wg sync.WaitGroup 274 b.ReportAllocs() 275 b.ResetTimer() 276 for i := 0; i < b.N; i++ { 277 wg.Add(benchmarkTimes) 278 for j := 0; j < benchmarkTimes; j++ { 279 p.CtxGo(context.Background(), func(_ context.Context) { 280 testFunc() 281 wg.Done() 282 }) 283 } 284 wg.Wait() 285 } 286 }) 287 // "github.com/bytedance/gopkg/util/gopool" 288 // b.Run("GoPool_gopkg", func(b *testing.B) { 289 // p := gopool.NewPool("", int32(runtime.GOMAXPROCS(0)), gopool.NewConfig()) 290 // var wg sync.WaitGroup 291 // b.ReportAllocs() 292 // b.ResetTimer() 293 // for i := 0; i < b.N; i++ { 294 // wg.Add(benchmarkTimes) 295 // for j := 0; j < benchmarkTimes; j++ { 296 // p.Go(func() { 297 // testFunc() 298 // wg.Done() 299 // }) 300 // } 301 // wg.Wait() 302 // } 303 // }) 304 b.Run("StdGo", func(b *testing.B) { 305 var wg sync.WaitGroup 306 b.ReportAllocs() 307 b.ResetTimer() 308 for i := 0; i < b.N; i++ { 309 wg.Add(benchmarkTimes) 310 for j := 0; j < benchmarkTimes; j++ { 311 go func() { 312 testFunc() 313 wg.Done() 314 }() 315 } 316 wg.Wait() 317 } 318 }) 319 } 320 321 /* 322 goos: windows 323 goarch: amd64 324 pkg: github.com/Aoi-hosizora/ahlib/xgopool 325 cpu: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz 326 BenchmarkPool 327 BenchmarkPool/GoPool_xgopool 328 BenchmarkPool/GoPool_xgopool-8 52 20352171 ns/op 383157 B/op 22036 allocs/op 329 BenchmarkPool/GoPool_gopkg 330 BenchmarkPool/GoPool_gopkg-8 128 18193410 ns/op 367304 B/op 22530 allocs/op 331 BenchmarkPool/StdGo 332 BenchmarkPool/StdGo-8 33 70946273 ns/op 320000 B/op 20000 allocs/op 333 PASS 334 */