github.com/celestiaorg/celestia-node@v0.15.0-beta.1/share/p2p/peers/timedqueue.go (about) 1 package peers 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/benbjohnson/clock" 8 "github.com/libp2p/go-libp2p/core/peer" 9 ) 10 11 // timedQueue store items for ttl duration and releases it with calling onPop callback. Each item 12 // is tracked independently 13 type timedQueue struct { 14 sync.Mutex 15 items []item 16 17 // ttl is the amount of time each item exist in the timedQueue 18 ttl time.Duration 19 clock clock.Clock 20 after *clock.Timer 21 // onPop will be called on item peer.ID after it is released 22 onPop func(peer.ID) 23 } 24 25 type item struct { 26 peer.ID 27 createdAt time.Time 28 } 29 30 func newTimedQueue(ttl time.Duration, onPop func(peer.ID)) *timedQueue { 31 return &timedQueue{ 32 items: make([]item, 0), 33 clock: clock.New(), 34 ttl: ttl, 35 onPop: onPop, 36 } 37 } 38 39 // releaseExpired will release all expired items 40 func (q *timedQueue) releaseExpired() { 41 q.Lock() 42 defer q.Unlock() 43 q.releaseUnsafe() 44 } 45 46 func (q *timedQueue) releaseUnsafe() { 47 if len(q.items) == 0 { 48 return 49 } 50 51 var i int 52 for _, next := range q.items { 53 timeIn := q.clock.Since(next.createdAt) 54 if timeIn < q.ttl { 55 // item is not expired yet, create a timer that will call releaseExpired 56 q.after.Stop() 57 q.after = q.clock.AfterFunc(q.ttl-timeIn, q.releaseExpired) 58 break 59 } 60 61 // item is expired 62 q.onPop(next.ID) 63 i++ 64 } 65 66 if i > 0 { 67 copy(q.items, q.items[i:]) 68 q.items = q.items[:len(q.items)-i] 69 } 70 } 71 72 func (q *timedQueue) push(peerID peer.ID) { 73 q.Lock() 74 defer q.Unlock() 75 76 q.items = append(q.items, item{ 77 ID: peerID, 78 createdAt: q.clock.Now(), 79 }) 80 81 // if it is the first item in queue, create a timer to call releaseExpired after its expiration 82 if len(q.items) == 1 { 83 q.after = q.clock.AfterFunc(q.ttl, q.releaseExpired) 84 } 85 } 86 87 func (q *timedQueue) len() int { 88 q.Lock() 89 defer q.Unlock() 90 return len(q.items) 91 }