github.com/weedge/lib@v0.0.0-20230424045628-a36dcc1d90e4/container/queue/delay_queue.go (about) 1 // from: https://github.com/RussellLuo/timingwheel/blob/master/delayqueue/delayqueue.go 2 package queue 3 4 import ( 5 "container/heap" 6 "sync" 7 "sync/atomic" 8 "time" 9 ) 10 11 // DelayQueue is an unbounded blocking queue of *Delayed* elements, in which 12 // an element can only be taken when its delay has expired. The head of the 13 // queue is the *Delayed* element whose delay expired furthest in the past. 14 type DelayQueue struct { 15 C chan interface{} 16 17 mu sync.Mutex 18 pq PriorityQueue 19 20 // Similar to the sleeping state of runtime.timers. 21 sleeping int32 22 wakeupC chan struct{} 23 } 24 25 // New creates an instance of delayQueue with the specified size. 26 func NewDelayQueue(size int) *DelayQueue { 27 return &DelayQueue{ 28 C: make(chan interface{}), 29 pq: NewPriorityQueue(size), 30 wakeupC: make(chan struct{}), 31 } 32 } 33 34 // Offer inserts the element into the current queue. 35 func (dq *DelayQueue) Offer(elem interface{}, expiration int64) { 36 item := &Item{Value: elem, Priority: expiration} 37 38 dq.mu.Lock() 39 heap.Push(&dq.pq, item) 40 index := item.Index 41 dq.mu.Unlock() 42 43 if index == 0 { 44 // A new item with the earliest expiration is added. 45 if atomic.CompareAndSwapInt32(&dq.sleeping, 1, 0) { 46 dq.wakeupC <- struct{}{} 47 } 48 } 49 } 50 51 // Poll starts an infinite loop, in which it continually waits for an element 52 // to expire and then send the expired element to the channel C. 53 func (dq *DelayQueue) Poll(exitC chan struct{}, nowF func() int64) { 54 for { 55 now := nowF() 56 57 dq.mu.Lock() 58 item, delta := dq.pq.PeekAndShift(now) 59 if item == nil { 60 // No items left or at least one item is pending. 61 62 // We must ensure the atomicity of the whole operation, which is 63 // composed of the above PeekAndShift and the following StoreInt32, 64 // to avoid possible race conditions between Offer and Poll. 65 atomic.StoreInt32(&dq.sleeping, 1) 66 } 67 dq.mu.Unlock() 68 69 if item == nil { 70 if delta == 0 { 71 // No items left. 72 select { 73 case <-dq.wakeupC: 74 // Wait until a new item is added. 75 continue 76 case <-exitC: 77 goto exit 78 } 79 } else if delta > 0 { 80 // At least one item is pending. 81 select { 82 case <-dq.wakeupC: 83 // A new item with an "earlier" expiration than the current "earliest" one is added. 84 continue 85 case <-time.After(time.Duration(delta) * time.Millisecond): // delay unit ms 86 // The current "earliest" item expires. 87 88 // Reset the sleeping state since there's no need to receive from wakeupC. 89 if atomic.SwapInt32(&dq.sleeping, 0) == 0 { 90 // A caller of Offer() is being blocked on sending to wakeupC, 91 // drain wakeupC to unblock the caller. 92 <-dq.wakeupC 93 } 94 continue 95 case <-exitC: 96 goto exit 97 } 98 } 99 } 100 101 select { 102 case dq.C <- item.Value: 103 // The expired element has been sent out successfully. 104 case <-exitC: 105 goto exit 106 } 107 } 108 109 exit: 110 // Reset the states 111 atomic.StoreInt32(&dq.sleeping, 0) 112 //println("exit dq Poll") 113 } 114 115 // from delay queue ch get elem to do 116 func (dq *DelayQueue) Do(exitC chan struct{}, do func(interface{})) { 117 for { 118 select { 119 case elem := <-dq.C: 120 do(elem) 121 case <-exitC: 122 //println("exit dq Do") 123 return 124 } 125 } 126 }