Initial QSfera import
This commit is contained in:
+231
@@ -0,0 +1,231 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
mutex sync.Mutex
|
||||
timers []*Timer
|
||||
rescheduleC = make(chan struct{}, 1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
go timerRoutine()
|
||||
}
|
||||
|
||||
// when is a helper function for setting the 'when' field of a runtimeTimer.
|
||||
// It returns what the time will be, in nanoseconds, Duration d in the future.
|
||||
// If d is negative, it is ignored. If the returned value would be less than
|
||||
// zero because of an overflow, MaxInt64 is returned.
|
||||
func when(d time.Duration) int64 {
|
||||
if d <= 0 {
|
||||
return time.Now().UnixNano()
|
||||
}
|
||||
t := time.Now().UnixNano() + int64(d)
|
||||
if t < 0 {
|
||||
t = 1<<63 - 1 // math.MaxInt64
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Add the timer to the heap.
|
||||
func addTimer(t *Timer, d time.Duration) {
|
||||
t.when = when(d)
|
||||
|
||||
mutex.Lock()
|
||||
addTimerLocked(t)
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func addTimerLocked(t *Timer) {
|
||||
t.i = len(timers)
|
||||
timers = append(timers, t)
|
||||
siftupTimer(t.i)
|
||||
|
||||
// Reschedule if this is the next timer in the heap.
|
||||
if t.i == 0 {
|
||||
reschedule()
|
||||
}
|
||||
}
|
||||
|
||||
// Delete timer t from the heap.
|
||||
// It returns true if t was removed, false if t wasn't even there.
|
||||
// Do not need to update the timer routine: if it wakes up early, no big deal.
|
||||
func delTimer(t *Timer) (b bool) {
|
||||
mutex.Lock()
|
||||
b = delTimerLocked(t)
|
||||
mutex.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Delete timer t from the heap.
|
||||
// It returns true if t was removed, false if t wasn't even there.
|
||||
// Do not need to update the timer routine: if it wakes up early, no big deal.
|
||||
func delTimerLocked(t *Timer) bool {
|
||||
// t may not be registered anymore and may have
|
||||
// a bogus i (typically 0, if generated by Go).
|
||||
// Verify it before proceeding.
|
||||
i := t.i
|
||||
last := len(timers) - 1
|
||||
if i < 0 || i > last || timers[i] != t {
|
||||
return false
|
||||
}
|
||||
if i != last {
|
||||
timers[i] = timers[last]
|
||||
timers[i].i = i
|
||||
}
|
||||
timers[last] = nil
|
||||
timers = timers[:last]
|
||||
if i != last {
|
||||
siftupTimer(i)
|
||||
siftdownTimer(i)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Reset the timer to the new timeout duration.
|
||||
// This clears the channel.
|
||||
func resetTimer(t *Timer, d time.Duration) (b bool) {
|
||||
mutex.Lock()
|
||||
b = delTimerLocked(t)
|
||||
t.reset()
|
||||
t.when = when(d)
|
||||
addTimerLocked(t)
|
||||
mutex.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func reschedule() {
|
||||
// Do not block if there is already a pending reschedule request.
|
||||
select {
|
||||
case rescheduleC <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func timerRoutine() {
|
||||
var now time.Time
|
||||
var delta int64
|
||||
var last int
|
||||
|
||||
var sleepTimerActive bool
|
||||
sleepTimer := time.NewTimer(time.Second)
|
||||
sleepTimer.Stop()
|
||||
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case <-sleepTimer.C:
|
||||
|
||||
case <-rescheduleC:
|
||||
// If not yet received a value from sleepTimer.C, the timer must be
|
||||
// stopped and—if Stop reports that the timer expired before being
|
||||
// stopped—the channel explicitly drained.
|
||||
if !sleepTimer.Stop() && sleepTimerActive {
|
||||
<-sleepTimer.C
|
||||
}
|
||||
}
|
||||
sleepTimerActive = false
|
||||
|
||||
Reschedule:
|
||||
now = time.Now()
|
||||
|
||||
mutex.Lock()
|
||||
if len(timers) == 0 {
|
||||
mutex.Unlock()
|
||||
continue Loop
|
||||
}
|
||||
|
||||
t := timers[0]
|
||||
delta = t.when - now.UnixNano()
|
||||
|
||||
// Sleep if not expired.
|
||||
if delta > 0 {
|
||||
mutex.Unlock()
|
||||
sleepTimer.Reset(time.Duration(delta))
|
||||
sleepTimerActive = true
|
||||
continue Loop
|
||||
}
|
||||
|
||||
// Timer expired. Trigger the timer's function callback.
|
||||
t.f(&now)
|
||||
|
||||
// Remove from heap.
|
||||
last = len(timers) - 1
|
||||
if last > 0 {
|
||||
timers[0] = timers[last]
|
||||
timers[0].i = 0
|
||||
}
|
||||
timers[last] = nil
|
||||
timers = timers[:last]
|
||||
if last > 0 {
|
||||
siftdownTimer(0)
|
||||
}
|
||||
t.i = -1 // mark as removed
|
||||
|
||||
mutex.Unlock()
|
||||
|
||||
// Reschedule immediately.
|
||||
goto Reschedule
|
||||
}
|
||||
}
|
||||
|
||||
// Heap maintenance algorithms.
|
||||
// Based on golang source /runtime/time.go
|
||||
|
||||
func siftupTimer(i int) {
|
||||
tmp := timers[i]
|
||||
when := tmp.when
|
||||
|
||||
var p int
|
||||
for i > 0 {
|
||||
p = (i - 1) / 4 // parent
|
||||
if when >= timers[p].when {
|
||||
break
|
||||
}
|
||||
timers[i] = timers[p]
|
||||
timers[i].i = i
|
||||
timers[p] = tmp
|
||||
timers[p].i = p
|
||||
i = p
|
||||
}
|
||||
}
|
||||
|
||||
func siftdownTimer(i int) {
|
||||
n := len(timers)
|
||||
when := timers[i].when
|
||||
tmp := timers[i]
|
||||
for {
|
||||
c := i*4 + 1 // left child
|
||||
c3 := c + 2 // mid child
|
||||
if c >= n {
|
||||
break
|
||||
}
|
||||
w := timers[c].when
|
||||
if c+1 < n && timers[c+1].when < w {
|
||||
w = timers[c+1].when
|
||||
c++
|
||||
}
|
||||
if c3 < n {
|
||||
w3 := timers[c3].when
|
||||
if c3+1 < n && timers[c3+1].when < w3 {
|
||||
w3 = timers[c3+1].when
|
||||
c3++
|
||||
}
|
||||
if w3 < w {
|
||||
w = w3
|
||||
c = c3
|
||||
}
|
||||
}
|
||||
if w >= when {
|
||||
break
|
||||
}
|
||||
timers[i] = timers[c]
|
||||
timers[i].i = i
|
||||
timers[c] = tmp
|
||||
timers[c].i = c
|
||||
i = c
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user