github.com/kaydxh/golang@v0.0.131/go/container/workqueue/queue.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package workqueue 23 24 import ( 25 "sync" 26 ) 27 28 type Interface interface { 29 Add(item interface{}) 30 Len() int 31 Get() (item interface{}, shutdown bool) 32 Done(item interface{}) 33 ShutDown() 34 ShuttingDown() bool 35 } 36 37 func NewQueue() *Type { 38 t := &Type{ 39 dirty: set{}, 40 processing: set{}, 41 cond: sync.NewCond(&sync.Mutex{}), 42 } 43 44 return t 45 } 46 47 // Type is a work queue (see the package comment). 48 type Type struct { 49 // queue defines the order in which we will work on items. Every 50 // element of queue should be in the dirty set and not in the 51 // processing set. 52 queue []t 53 54 // dirty defines all of the items that need to be processed. 55 dirty set 56 57 // Things that are currently being processed are in the processing set. 58 // These things may be simultaneously in the dirty set. When we finish 59 // processing something and remove it from this set, we'll check if 60 // it's in the dirty set, and if so, add it to the queue. 61 processing set 62 63 cond *sync.Cond 64 65 shuttingDown bool 66 drain bool 67 } 68 69 type empty struct{} 70 type t interface{} 71 type set map[t]empty 72 73 func (s set) has(item t) bool { 74 _, exists := s[item] 75 return exists 76 } 77 78 func (s set) insert(item t) { 79 s[item] = empty{} 80 } 81 82 func (s set) delete(item t) { 83 delete(s, item) 84 } 85 86 func (s set) len() int { 87 return len(s) 88 } 89 90 // Add marks item as needing processing. 91 func (q *Type) Add(item interface{}) { 92 q.cond.L.Lock() 93 defer q.cond.L.Unlock() 94 if q.shuttingDown { 95 return 96 } 97 if q.dirty.has(item) { 98 return 99 } 100 101 q.dirty.insert(item) 102 if q.processing.has(item) { 103 return 104 } 105 106 q.queue = append(q.queue, item) 107 q.cond.Signal() 108 } 109 110 func (q *Type) Len() int { 111 q.cond.L.Lock() 112 defer q.cond.L.Unlock() 113 return len(q.queue) 114 } 115 116 func (q *Type) Get() (item interface{}, shutdown bool) { 117 q.cond.L.Lock() 118 defer q.cond.L.Unlock() 119 for len(q.queue) == 0 && !q.shuttingDown { 120 q.cond.Wait() 121 } 122 if len(q.queue) == 0 { 123 // We must be shutting down. 124 return nil, true 125 } 126 127 item = q.queue[0] 128 // The underlying array still exists and reference this object, so the object will not be garbage collected. 129 q.queue[0] = nil 130 q.queue = q.queue[1:] 131 132 q.processing.insert(item) 133 q.dirty.delete(item) 134 135 return item, false 136 } 137 138 func (q *Type) Done(item interface{}) { 139 q.cond.L.Lock() 140 defer q.cond.L.Unlock() 141 142 q.processing.delete(item) 143 if q.dirty.has(item) { 144 q.queue = append(q.queue, item) 145 q.cond.Signal() 146 } else if q.processing.len() == 0 { 147 q.cond.Signal() 148 } 149 } 150 151 // ShutDown will cause q to ignore all new items added to it and 152 // immediately instruct the worker goroutines to exit. 153 func (q *Type) ShutDown() { 154 q.setDrain(false) 155 q.shutdown() 156 } 157 158 // ShutDownWithDrain will cause q to ignore all new items added to it. As soon 159 // as the worker goroutines have "drained", i.e: finished processing and called 160 // Done on all existing items in the queue; they will be instructed to exit and 161 // ShutDownWithDrain will return. Hence: a strict requirement for using this is; 162 // your workers must ensure that Done is called on all items in the queue once 163 // the shut down has been initiated, if that is not the case: this will block 164 // indefinitely. It is, however, safe to call ShutDown after having called 165 // ShutDownWithDrain, as to force the queue shut down to terminate immediately 166 // without waiting for the drainage. 167 func (q *Type) ShutDownWithDrain() { 168 q.setDrain(true) 169 q.shutdown() 170 for q.isProcessing() && q.shouldDrain() { 171 q.waitForProcessing() 172 } 173 } 174 175 // isProcessing indicates if there are still items on the work queue being 176 // processed. It's used to drain the work queue on an eventual shutdown. 177 func (q *Type) isProcessing() bool { 178 q.cond.L.Lock() 179 defer q.cond.L.Unlock() 180 return q.processing.len() != 0 181 } 182 183 // waitForProcessing waits for the worker goroutines to finish processing items 184 // and call Done on them. 185 func (q *Type) waitForProcessing() { 186 q.cond.L.Lock() 187 defer q.cond.L.Unlock() 188 // Ensure that we do not wait on a queue which is already empty, as that 189 // could result in waiting for Done to be called on items in an empty queue 190 // which has already been shut down, which will result in waiting 191 // indefinitely. 192 if q.processing.len() == 0 { 193 return 194 } 195 q.cond.Wait() 196 } 197 198 func (q *Type) setDrain(shouldDrain bool) { 199 q.cond.L.Lock() 200 defer q.cond.L.Unlock() 201 q.drain = shouldDrain 202 } 203 204 func (q *Type) shouldDrain() bool { 205 q.cond.L.Lock() 206 defer q.cond.L.Unlock() 207 return q.drain 208 } 209 210 func (q *Type) shutdown() { 211 q.cond.L.Lock() 212 defer q.cond.L.Unlock() 213 q.shuttingDown = true 214 q.cond.Broadcast() 215 } 216 217 func (q *Type) ShuttingDown() bool { 218 q.cond.L.Lock() 219 defer q.cond.L.Unlock() 220 221 return q.shuttingDown 222 }