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 } }