gitlab.com/ignitionrobotics/web/ign-go@v1.0.0-rc4/queue.go (about) 1 package ign 2 3 import ( 4 "sync" 5 "time" 6 ) 7 8 const ( 9 // WaitForNextElementChanCapacity is being used to set the maximum capacity of listeners 10 WaitForNextElementChanCapacity = 1000 11 // dequeueOrWaitForNextElementInvokeGapTime is being used to set the time between dequeue attempts. 12 dequeueOrWaitForNextElementInvokeGapTime = 10 13 ) 14 15 // Queue is a thread-safe data type that uses an underlying slice as a queue. 16 // It provides a set of methods to modify the elements of the queue. 17 // It was created to replace a go channel since channels don't let you modify their elements order. 18 // You can push elements inside the queue using Enqueue method. They will be pushed to the back of the queue. 19 // You can pop elements from the queue using both Dequeue() and DequeueOrWaitForNextElement(). 20 // DequeueOrWaitForNextElement() waits until the queue has any element inside to pop it out. 21 type Queue struct { 22 // slice represents the actual queue. 23 slice []interface{} 24 // rwmutex represents the read-write lock for the queue. 25 rwmutex sync.RWMutex 26 // queue for watchers that will wait for next elements (if queue is empty at DequeueOrWaitForNextElement execution) 27 waitForNextElementChan chan chan interface{} 28 } 29 30 // NewQueue returns a new Queue instance. 31 func NewQueue() (queue *Queue) { 32 queue = &Queue{} 33 queue.initialize() 34 return 35 } 36 37 // initialize initializes the queue properties. 38 func (st *Queue) initialize() { 39 st.slice = make([]interface{}, 0) 40 st.waitForNextElementChan = make(chan chan interface{}, WaitForNextElementChanCapacity) 41 } 42 43 // Enqueue enqueues an element. 44 func (st *Queue) Enqueue(value interface{}) { 45 // check if there is a listener waiting for the next element (this element) 46 select { 47 case listener := <-st.waitForNextElementChan: 48 // send the element through the listener's channel instead of enqueue it 49 select { 50 case listener <- value: 51 default: 52 st.enqueue(value, true) 53 } 54 55 default: 56 st.enqueue(value, true) 57 } 58 } 59 60 // enqueue appends a value to the queue 61 func (st *Queue) enqueue(value interface{}, lock bool) { 62 if lock { 63 st.rwmutex.Lock() 64 defer st.rwmutex.Unlock() 65 } 66 67 st.slice = append(st.slice, value) 68 } 69 70 // Dequeue dequeues an element. Returns error if queue is locked or empty. 71 func (st *Queue) Dequeue() (interface{}, *ErrMsg) { 72 st.rwmutex.Lock() 73 defer st.rwmutex.Unlock() 74 75 len := len(st.slice) 76 if len == 0 { 77 return nil, NewErrorMessage(ErrorQueueEmpty) 78 } 79 80 element := st.slice[0] 81 st.slice = st.slice[1:] 82 83 return element, nil 84 } 85 86 // DequeueOrWaitForNextElement dequeues an element (if exist) or waits until the next element gets enqueued and returns it. 87 // Multiple calls to DequeueOrWaitForNextElement() would enqueue multiple "listeners" for future enqueued elements. 88 func (st *Queue) DequeueOrWaitForNextElement() (interface{}, *ErrMsg) { 89 for { 90 // get the slice's len 91 st.rwmutex.Lock() 92 length := len(st.slice) 93 st.rwmutex.Unlock() 94 95 if length == 0 { 96 // channel to wait for next enqueued element 97 waitChan := make(chan interface{}) 98 99 select { 100 // enqueue a watcher into the watchForNextElementChannel to wait for the next element 101 case st.waitForNextElementChan <- waitChan: 102 103 // re-checks every i milliseconds (top: 10 times) ... the following verifies if an item was enqueued 104 // around the same time DequeueOrWaitForNextElement was invoked, meaning the waitChan wasn't yet sent over 105 // st.waitForNextElementChan 106 for i := 0; i < dequeueOrWaitForNextElementInvokeGapTime; i++ { 107 select { 108 case dequeuedItem := <-waitChan: 109 return dequeuedItem, nil 110 case <-time.After(time.Millisecond * time.Duration(i)): 111 if dequeuedItem, err := st.Dequeue(); err == nil { 112 return dequeuedItem, nil 113 } 114 } 115 } 116 117 // return the next enqueued element, if any 118 return <-waitChan, nil 119 default: 120 // too many watchers (waitForNextElementChanCapacity) enqueued waiting for next elements 121 return nil, NewErrorMessage(ErrorQueueTooManyListeners) 122 } 123 } 124 125 st.rwmutex.Lock() 126 127 // verify that at least 1 item resides on the queue 128 if len(st.slice) == 0 { 129 st.rwmutex.Unlock() 130 continue 131 } 132 elementToReturn := st.slice[0] 133 st.slice = st.slice[1:] 134 135 st.rwmutex.Unlock() 136 return elementToReturn, nil 137 } 138 } 139 140 // GetElement returns an element's value and keeps the element at the queue 141 func (st *Queue) GetElement(index int) (interface{}, *ErrMsg) { 142 st.rwmutex.RLock() 143 defer st.rwmutex.RUnlock() 144 145 if len(st.slice) <= index { 146 return nil, NewErrorMessage(ErrorQueueIndexOutOfBounds) 147 } 148 149 return st.slice[index], nil 150 } 151 152 // GetElements returns the entire list of elements from the queue 153 func (st *Queue) GetElements() ([]interface{}, *ErrMsg) { 154 155 st.rwmutex.RLock() 156 defer st.rwmutex.RUnlock() 157 158 return st.slice, nil 159 } 160 161 // GetFilteredElements returns a subset list from the queue 162 func (st *Queue) GetFilteredElements(offset, limit int) ([]interface{}, *ErrMsg) { 163 st.rwmutex.Lock() 164 defer st.rwmutex.Unlock() 165 166 length := len(st.slice) 167 168 if length == 0 { 169 return st.slice, nil 170 } 171 172 if offset >= length || offset < 0 || limit <= 0 { 173 return nil, NewErrorMessage(ErrorQueueIndexOutOfBounds) 174 } 175 176 if (offset + limit) >= length { 177 limit = len(st.slice) - offset 178 } 179 low := offset 180 high := offset + limit 181 subset := st.slice[low:high] 182 183 return subset, nil 184 } 185 186 // Find returns a list of ids of the elements that match the given criteria. 187 // Returns an empty slice if there are not elements that match. 188 func (st *Queue) Find(criteria func(element interface{}) bool) []int { 189 return st.find(criteria, true) 190 } 191 192 // find returns a list of ids of the elements that match the given criteria. 193 func (st *Queue) find(criteria func(element interface{}) bool, lock bool) (result []int) { 194 if lock { 195 st.rwmutex.Lock() 196 defer st.rwmutex.Unlock() 197 } 198 199 for id, item := range st.slice { 200 if criteria(item) { 201 result = append(result, id) 202 } 203 } 204 205 return 206 } 207 208 // FindOne returns the id from a given element. 209 // Returns -1 if the element does not exist in the queue. 210 func (st *Queue) FindOne(target interface{}) int { 211 return st.findOne(target, true) 212 } 213 214 // findOne returns the id from a given element. 215 func (st *Queue) findOne(target interface{}, lock bool) int { 216 if lock { 217 st.rwmutex.Lock() 218 defer st.rwmutex.Unlock() 219 } 220 221 for id, item := range st.slice { 222 if item == target { 223 return id 224 } 225 } 226 227 return -1 228 } 229 230 // FindByIDs returns a list of elements of the given ids. 231 // Returns an empty slice if there are no elements in the queue. 232 func (st *Queue) FindByIDs(ids []int) []interface{} { 233 return st.findByIDs(ids, true) 234 } 235 236 // findByIDs returns a list of elements of the given ids. 237 func (st *Queue) findByIDs(ids []int, lock bool) (result []interface{}) { 238 if lock { 239 st.rwmutex.Lock() 240 defer st.rwmutex.Unlock() 241 } 242 243 length := len(ids) 244 count := 0 245 246 for id, item := range st.slice { 247 for _, i := range ids { 248 if id == i { 249 result = append(result, item) 250 count++ 251 break 252 } 253 } 254 255 if count == length { 256 break 257 } 258 } 259 260 return 261 } 262 263 // Remove removes an element from the queue 264 func (st *Queue) Remove(target interface{}) *ErrMsg { 265 st.rwmutex.Lock() 266 defer st.rwmutex.Unlock() 267 268 index := st.findOne(target, false) 269 270 if index == -1 { 271 return NewErrorMessage(ErrorIDNotFound) 272 } 273 274 // remove the element 275 st.slice = append(st.slice[:index], st.slice[index+1:]...) 276 277 return nil 278 } 279 280 // GetLen returns the number of enqueued elements 281 func (st *Queue) GetLen() int { 282 st.rwmutex.RLock() 283 defer st.rwmutex.RUnlock() 284 285 return len(st.slice) 286 } 287 288 // GetCap returns the queue's capacity 289 func (st *Queue) GetCap() int { 290 st.rwmutex.RLock() 291 defer st.rwmutex.RUnlock() 292 293 return cap(st.slice) 294 } 295 296 // Swap swaps values A and B. 297 func (st *Queue) Swap(a interface{}, b interface{}) *ErrMsg { 298 st.rwmutex.Lock() 299 defer st.rwmutex.Unlock() 300 301 length := len(st.slice) 302 if length == 0 { 303 return NewErrorMessage(ErrorQueueEmpty) 304 } 305 306 aIndex := st.findOne(a, false) 307 bIndex := st.findOne(b, false) 308 309 if aIndex == -1 || bIndex == -1 { 310 return NewErrorMessage(ErrorIDNotFound) 311 } 312 313 if aIndex == bIndex { 314 return NewErrorMessage(ErrorQueueSwapIndexesMatch) 315 } 316 317 st.slice[aIndex], st.slice[bIndex] = st.slice[bIndex], st.slice[aIndex] 318 319 return nil 320 } 321 322 // MoveToFront moves an element to the front of the queue 323 func (st *Queue) MoveToFront(target interface{}) *ErrMsg { 324 st.rwmutex.Lock() 325 defer st.rwmutex.Unlock() 326 327 length := len(st.slice) 328 if length == 0 { 329 return NewErrorMessage(ErrorQueueEmpty) 330 } 331 332 index := st.findOne(target, false) 333 if index == -1 { 334 return NewErrorMessage(ErrorIDNotFound) 335 } 336 337 if index == 0 { 338 return NewErrorMessage(ErrorQueueMoveIndexFrontPosition) 339 } 340 341 // Moves the element all the way to the back of the queue. 342 // The element is moved one position at a time using bubble sort algorithm. 343 for i := index; i >= 1; i-- { 344 st.slice[i], st.slice[i-1] = st.slice[i-1], st.slice[i] 345 } 346 return nil 347 } 348 349 // MoveToBack moves an element to the back of the queue 350 func (st *Queue) MoveToBack(target interface{}) *ErrMsg { 351 st.rwmutex.Lock() 352 defer st.rwmutex.Unlock() 353 354 length := len(st.slice) 355 if length == 0 { 356 return NewErrorMessage(ErrorQueueEmpty) 357 } 358 359 index := st.findOne(target, false) 360 361 if index == -1 { 362 return NewErrorMessage(ErrorIDNotFound) 363 } 364 365 if index == length-1 { 366 return NewErrorMessage(ErrorQueueMoveIndexBackPosition) 367 } 368 369 // Moves the element all the way to the back of the queue. 370 // The element is moved one position at a time using bubble sort algorithm. 371 for i := index; i < length-1; i++ { 372 st.slice[i], st.slice[i+1] = st.slice[i+1], st.slice[i] 373 } 374 375 return nil 376 }