github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/workerpool/async_pool_test.go (about) 1 // Copyright 2020 PingCAP, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package workerpool 15 16 import ( 17 "context" 18 "math/rand" 19 "sync" 20 "sync/atomic" 21 "time" 22 23 "github.com/pingcap/log" 24 25 "github.com/pingcap/check" 26 "github.com/pingcap/errors" 27 "github.com/pingcap/ticdc/pkg/util/testleak" 28 "golang.org/x/sync/errgroup" 29 ) 30 31 type asyncPoolSuite struct{} 32 33 var _ = check.Suite(&asyncPoolSuite{}) 34 35 func (s *asyncPoolSuite) TestBasic(c *check.C) { 36 defer testleak.AfterTest(c)() 37 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 38 defer cancel() 39 40 errg, ctx := errgroup.WithContext(ctx) 41 42 pool := newDefaultAsyncPoolImpl(4) 43 errg.Go(func() error { 44 return pool.Run(ctx) 45 }) 46 47 var sum int32 48 var wg sync.WaitGroup 49 for i := 0; i < 100; i++ { 50 wg.Add(1) 51 finalI := i 52 err := pool.Go(ctx, func() { 53 time.Sleep(time.Millisecond * time.Duration(rand.Int()%100)) 54 atomic.AddInt32(&sum, int32(finalI+1)) 55 wg.Done() 56 }) 57 c.Assert(err, check.IsNil) 58 } 59 60 wg.Wait() 61 c.Assert(sum, check.Equals, int32(5050)) 62 63 cancel() 64 err := errg.Wait() 65 c.Assert(err, check.ErrorMatches, "context canceled") 66 } 67 68 func (s *asyncPoolSuite) TestEventuallyRun(c *check.C) { 69 defer testleak.AfterTest(c)() 70 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 71 defer cancel() 72 73 errg, ctx := errgroup.WithContext(ctx) 74 loopCtx, cancelLoop := context.WithCancel(ctx) 75 defer cancelLoop() 76 77 pool := newDefaultAsyncPoolImpl(4) 78 errg.Go(func() error { 79 defer cancelLoop() 80 for i := 0; i < 10; i++ { 81 log.Info("running pool") 82 err := runForDuration(ctx, time.Millisecond*500, func(ctx context.Context) error { 83 return pool.Run(ctx) 84 }) 85 if err != nil { 86 return errors.Trace(err) 87 } 88 } 89 return nil 90 }) 91 92 var sum int32 93 var sumExpected int32 94 loop: 95 for i := 0; ; i++ { 96 select { 97 case <-loopCtx.Done(): 98 break loop 99 default: 100 } 101 finalI := i 102 err := pool.Go(loopCtx, func() { 103 if rand.Int()%128 == 0 { 104 time.Sleep(2 * time.Millisecond) 105 } 106 atomic.AddInt32(&sum, int32(finalI+1)) 107 }) 108 if err != nil { 109 c.Assert(err, check.ErrorMatches, "context canceled") 110 } else { 111 sumExpected += int32(i + 1) 112 } 113 } 114 115 cancel() 116 err := errg.Wait() 117 c.Assert(err, check.IsNil) 118 c.Assert(sum, check.Equals, sumExpected) 119 } 120 121 func runForDuration(ctx context.Context, duration time.Duration, f func(ctx context.Context) error) error { 122 timedCtx, cancel := context.WithTimeout(ctx, duration) 123 defer cancel() 124 125 errCh := make(chan error) 126 go func() { 127 errCh <- f(timedCtx) 128 }() 129 130 select { 131 case <-ctx.Done(): 132 return ctx.Err() 133 case err := <-errCh: 134 if errors.Cause(err) == context.DeadlineExceeded { 135 return nil 136 } 137 return errors.Trace(err) 138 } 139 }