mirror of https://github.com/poanetwork/gecko.git
143 lines
2.9 KiB
Go
143 lines
2.9 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package timer
|
|
|
|
import (
|
|
"container/list"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
)
|
|
|
|
type timeoutHandler func()
|
|
|
|
type timeout struct {
|
|
id ids.ID
|
|
handler timeoutHandler
|
|
timer time.Time
|
|
}
|
|
|
|
// TimeoutManager is a manager for timeouts.
|
|
type TimeoutManager struct {
|
|
lock sync.Mutex
|
|
duration time.Duration // Amount of time before a timeout
|
|
timeoutMap map[[32]byte]*list.Element
|
|
timeoutList *list.List
|
|
timer *Timer // Timer that will fire to clear the timeouts
|
|
}
|
|
|
|
// Initialize is a constructor b/c Golang, in its wisdom, doesn't ... have them?
|
|
func (tm *TimeoutManager) Initialize(duration time.Duration) {
|
|
tm.duration = duration
|
|
tm.timeoutMap = make(map[[32]byte]*list.Element)
|
|
tm.timeoutList = list.New()
|
|
tm.timer = NewTimer(tm.Timeout)
|
|
}
|
|
|
|
// Dispatch ...
|
|
func (tm *TimeoutManager) Dispatch() { tm.timer.Dispatch() }
|
|
|
|
// Stop executing timeouts
|
|
func (tm *TimeoutManager) Stop() { tm.timer.Stop() }
|
|
|
|
// Put puts hash into the hash map
|
|
func (tm *TimeoutManager) Put(id ids.ID, handler func()) {
|
|
tm.lock.Lock()
|
|
defer tm.lock.Unlock()
|
|
|
|
tm.put(id, handler)
|
|
}
|
|
|
|
// Remove the item that no longer needs to be there.
|
|
func (tm *TimeoutManager) Remove(id ids.ID) {
|
|
tm.lock.Lock()
|
|
defer tm.lock.Unlock()
|
|
|
|
tm.remove(id)
|
|
}
|
|
|
|
// Timeout registers a timeout
|
|
func (tm *TimeoutManager) Timeout() {
|
|
tm.lock.Lock()
|
|
defer tm.lock.Unlock()
|
|
|
|
tm.timeout()
|
|
}
|
|
|
|
func (tm *TimeoutManager) timeout() {
|
|
timeBound := time.Now().Add(-tm.duration)
|
|
// removeExpiredHead returns false once there is nothing left to remove
|
|
for {
|
|
timeout := tm.removeExpiredHead(timeBound)
|
|
if timeout == nil {
|
|
break
|
|
}
|
|
|
|
// Don't execute a callback with a lock held
|
|
tm.lock.Unlock()
|
|
timeout()
|
|
tm.lock.Lock()
|
|
}
|
|
tm.registerTimeout()
|
|
}
|
|
|
|
func (tm *TimeoutManager) put(id ids.ID, handler timeoutHandler) {
|
|
tm.remove(id)
|
|
|
|
tm.timeoutMap[id.Key()] = tm.timeoutList.PushBack(timeout{
|
|
id: id,
|
|
handler: handler,
|
|
timer: time.Now(),
|
|
})
|
|
|
|
if tm.timeoutList.Len() == 1 {
|
|
tm.registerTimeout()
|
|
}
|
|
}
|
|
|
|
func (tm *TimeoutManager) remove(id ids.ID) {
|
|
key := id.Key()
|
|
e, exists := tm.timeoutMap[key]
|
|
if !exists {
|
|
return
|
|
}
|
|
delete(tm.timeoutMap, key)
|
|
tm.timeoutList.Remove(e)
|
|
}
|
|
|
|
// Returns true if the head was removed, false otherwise
|
|
func (tm *TimeoutManager) removeExpiredHead(t time.Time) func() {
|
|
if tm.timeoutList.Len() == 0 {
|
|
return nil
|
|
}
|
|
|
|
e := tm.timeoutList.Front()
|
|
head := e.Value.(timeout)
|
|
|
|
headTime := head.timer
|
|
if headTime.Before(t) {
|
|
tm.remove(head.id)
|
|
return head.handler
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tm *TimeoutManager) registerTimeout() {
|
|
if tm.timeoutList.Len() == 0 {
|
|
// There are no pending timeouts
|
|
tm.timer.Cancel()
|
|
return
|
|
}
|
|
|
|
e := tm.timeoutList.Front()
|
|
head := e.Value.(timeout)
|
|
|
|
timeBound := time.Now().Add(-tm.duration)
|
|
headTime := head.timer
|
|
duration := headTime.Sub(timeBound)
|
|
|
|
tm.timer.SetTimeoutIn(duration)
|
|
}
|