2018-08-14 03:05:20 +03:00
|
|
|
package expire
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Item is a something that can expire
|
|
|
|
type Item interface {
|
|
|
|
Expires() time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// List of expireable items
|
|
|
|
type List struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
queue queue
|
|
|
|
bgrun bool
|
|
|
|
Expired func(item Item)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push an item onto the queue
|
|
|
|
func (list *List) Push(item Item) {
|
|
|
|
unix := item.Expires().UnixNano()
|
|
|
|
list.mu.Lock()
|
|
|
|
if !list.bgrun {
|
|
|
|
list.bgrun = true
|
|
|
|
go list.bg()
|
|
|
|
}
|
|
|
|
list.queue.push(unix, item)
|
|
|
|
list.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *List) bg() {
|
2018-08-14 06:27:22 +03:00
|
|
|
now := time.Now().UnixNano()
|
2018-08-14 03:05:20 +03:00
|
|
|
for {
|
|
|
|
list.mu.Lock()
|
|
|
|
if list.queue.len == 0 {
|
|
|
|
list.bgrun = false
|
|
|
|
list.mu.Unlock()
|
|
|
|
break
|
|
|
|
}
|
2018-08-14 06:27:22 +03:00
|
|
|
if now > list.queue.peek().unix { // now.After(list.queue.peek().unix)
|
2018-08-14 03:05:20 +03:00
|
|
|
n := list.queue.pop()
|
2019-10-08 19:25:50 +03:00
|
|
|
exfn := list.Expired
|
2018-08-14 03:05:20 +03:00
|
|
|
list.mu.Unlock()
|
2019-10-08 19:25:50 +03:00
|
|
|
if exfn != nil {
|
|
|
|
exfn(n.item)
|
2018-08-14 03:05:20 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
list.mu.Unlock()
|
|
|
|
time.Sleep(time.Second / 10)
|
2018-08-14 06:27:22 +03:00
|
|
|
now = time.Now().UnixNano()
|
2018-08-14 03:05:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type qnode struct {
|
|
|
|
unix int64
|
|
|
|
item Item
|
|
|
|
}
|
|
|
|
|
|
|
|
type queue struct {
|
|
|
|
nodes []qnode
|
|
|
|
len int
|
|
|
|
size int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (q *queue) push(unix int64, item Item) {
|
|
|
|
if q.nodes == nil {
|
|
|
|
q.nodes = make([]qnode, 2)
|
|
|
|
} else {
|
|
|
|
q.nodes = append(q.nodes, qnode{})
|
|
|
|
}
|
|
|
|
i := q.len + 1
|
|
|
|
j := i / 2
|
|
|
|
for i > 1 && q.nodes[j].unix > unix {
|
|
|
|
q.nodes[i] = q.nodes[j]
|
|
|
|
i = j
|
|
|
|
j = j / 2
|
|
|
|
}
|
|
|
|
q.nodes[i].unix = unix
|
|
|
|
q.nodes[i].item = item
|
|
|
|
q.len++
|
|
|
|
}
|
|
|
|
|
|
|
|
func (q *queue) peek() qnode {
|
|
|
|
if q.len == 0 {
|
|
|
|
return qnode{}
|
|
|
|
}
|
|
|
|
return q.nodes[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (q *queue) pop() qnode {
|
|
|
|
if q.len == 0 {
|
|
|
|
return qnode{}
|
|
|
|
}
|
|
|
|
n := q.nodes[1]
|
|
|
|
q.nodes[1] = q.nodes[q.len]
|
|
|
|
q.len--
|
|
|
|
var j, k int
|
|
|
|
i := 1
|
|
|
|
for i != q.len+1 {
|
|
|
|
k = q.len + 1
|
|
|
|
j = 2 * i
|
|
|
|
if j <= q.len && q.nodes[j].unix < q.nodes[k].unix {
|
|
|
|
k = j
|
|
|
|
}
|
|
|
|
if j+1 <= q.len && q.nodes[j+1].unix < q.nodes[k].unix {
|
|
|
|
k = j + 1
|
|
|
|
}
|
|
|
|
q.nodes[i] = q.nodes[k]
|
|
|
|
i = k
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|