github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/sync/worker_pool.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 implements synchronization facililites such as worker pools. 22 package sync 23 24 import ( 25 "time" 26 27 "github.com/m3db/m3/src/dbnode/tracepoint" 28 "github.com/m3db/m3/src/x/context" 29 ) 30 31 type workerPool struct { 32 workCh chan struct{} 33 } 34 35 // NewWorkerPool creates a new worker pool. 36 func NewWorkerPool(size int) WorkerPool { 37 return &workerPool{workCh: make(chan struct{}, size)} 38 } 39 40 func (p *workerPool) Init() { 41 for i := 0; i < cap(p.workCh); i++ { 42 p.workCh <- struct{}{} 43 } 44 } 45 46 func (p *workerPool) Go(work Work) { 47 p.GoInstrument(work) 48 } 49 50 func (p *workerPool) GoInstrument(work Work) ScheduleResult { 51 start := time.Now() 52 token := <-p.workCh 53 wait := time.Since(start) 54 go func() { 55 work() 56 p.workCh <- token 57 }() 58 return ScheduleResult{ 59 Available: true, 60 WaitTime: wait, 61 } 62 } 63 64 func (p *workerPool) GoIfAvailable(work Work) bool { 65 select { 66 case token := <-p.workCh: 67 go func() { 68 work() 69 p.workCh <- token 70 }() 71 return true 72 default: 73 return false 74 } 75 } 76 77 func (p *workerPool) GoWithTimeout(work Work, timeout time.Duration) bool { 78 return p.GoWithTimeoutInstrument(work, timeout).Available 79 } 80 81 func (p *workerPool) GoWithTimeoutInstrument(work Work, timeout time.Duration) ScheduleResult { 82 // Attempt to try writing without allocating a ticker. 83 select { 84 case token := <-p.workCh: 85 go func() { 86 work() 87 p.workCh <- token 88 }() 89 return ScheduleResult{Available: true} 90 default: 91 } 92 93 // Now allocate a ticker and attempt a write. 94 ticker := time.NewTicker(timeout) 95 defer ticker.Stop() 96 97 start := time.Now() 98 select { 99 case token := <-p.workCh: 100 wait := time.Since(start) 101 go func() { 102 work() 103 p.workCh <- token 104 }() 105 return ScheduleResult{Available: true, WaitTime: wait} 106 case <-ticker.C: 107 return ScheduleResult{Available: false, WaitTime: timeout} 108 } 109 } 110 111 func (p *workerPool) GoWithContext(ctx context.Context, work Work) ScheduleResult { 112 stdctx := ctx.GoContext() 113 // Don't give out a token if the ctx has already been canceled. 114 select { 115 case <-stdctx.Done(): 116 return ScheduleResult{Available: false, WaitTime: 0} 117 default: 118 } 119 120 start := time.Now() 121 _, sp := ctx.StartTraceSpan(tracepoint.WorkerPoolWait) 122 123 select { 124 case token := <-p.workCh: 125 sp.Finish() 126 wait := time.Since(start) 127 go func() { 128 work() 129 p.workCh <- token 130 }() 131 return ScheduleResult{Available: true, WaitTime: wait} 132 case <-stdctx.Done(): 133 sp.Finish() 134 return ScheduleResult{Available: false, WaitTime: time.Since(start)} 135 } 136 } 137 138 func (p *workerPool) FastContextCheck(batchSize int) WorkerPool { 139 return &fastWorkerPool{workerPool: p, batchSize: batchSize} 140 } 141 142 func (p *workerPool) Size() int { 143 return cap(p.workCh) 144 }