// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package time import ( "container/heap" "sync" ) // The Timer type represents a single event. // When the Timer expires, the current time will be sent on C // unless the Timer represents an AfterFunc event. type Timer struct { C <-chan int64 t int64 // The absolute time that the event should fire. f func(int64) // The function to call when the event fires. i int // The event's index inside eventHeap. } type timerHeap []*Timer // forever is the absolute time (in ns) of an event that is forever away. const forever = 1 << 62 // maxSleepTime is the maximum length of time that a sleeper // sleeps for before checking if it is defunct. const maxSleepTime = 1e9 var ( // timerMutex guards the variables inside this var group. timerMutex sync.Mutex // timers holds a binary heap of pending events, terminated with a sentinel. timers timerHeap // currentSleeper is an ever-incrementing counter which represents // the current sleeper. It allows older sleepers to detect that they are // defunct and exit. currentSleeper int64 ) func init() { timers.Push(&Timer{t: forever}) // sentinel } // NewTimer creates a new Timer that will send // the current time on its channel after at least ns nanoseconds. func NewTimer(ns int64) *Timer { c := make(chan int64, 1) e := after(ns, func(t int64) { c <- t }) e.C = c return e } // After waits at least ns nanoseconds before sending the current time // on the returned channel. // It is equivalent to NewTimer(ns).C. func After(ns int64) <-chan int64 { return NewTimer(ns).C } // AfterFunc waits at least ns nanoseconds before calling f // in its own goroutine. It returns a Timer that can // be used to cancel the call using its Stop method. func AfterFunc(ns int64, f func()) *Timer { return after(ns, func(_ int64) { go f() }) } // Stop prevents the Timer from firing. // It returns true if the call stops the timer, false if the timer has already // expired or stopped. func (e *Timer) Stop() (ok bool) { timerMutex.Lock() // Avoid removing the first event in the queue so that // we don't start a new sleeper unnecessarily. if e.i > 0 { heap.Remove(timers, e.i) } ok = e.f != nil e.f = nil timerMutex.Unlock() return } // after is the implementation of After and AfterFunc. // When the current time is after ns, it calls f with the current time. // It assumes that f will not block. func after(ns int64, f func(int64)) (e *Timer) { now := Nanoseconds() t := now + ns if ns > 0 && t < now { panic("time: time overflow") } timerMutex.Lock() t0 := timers[0].t e = &Timer{nil, t, f, -1} heap.Push(timers, e) // Start a new sleeper if the new event is before // the first event in the queue. If the length of time // until the new event is at least maxSleepTime, // then we're guaranteed that the sleeper will wake up // in time to service it, so no new sleeper is needed. if t0 > t && (t0 == forever || ns < maxSleepTime) { currentSleeper++ go sleeper(currentSleeper) } timerMutex.Unlock() return } // sleeper continually looks at the earliest event in the queue, waits until it happens, // then removes any events in the queue that are due. It stops when the queue // is empty or when another sleeper has been started. func sleeper(sleeperId int64) { timerMutex.Lock() e := timers[0] t := Nanoseconds() for e.t != forever { if dt := e.t - t; dt > 0 { if dt > maxSleepTime { dt = maxSleepTime } timerMutex.Unlock() sysSleep(dt) timerMutex.Lock() if currentSleeper != sleeperId { // Another sleeper has been started, making this one redundant. break } } e = timers[0] t = Nanoseconds() for t >= e.t { if e.f != nil { e.f(t) e.f = nil } heap.Pop(timers) e = timers[0] } } timerMutex.Unlock() } func (timerHeap) Len() int { return len(timers) } func (timerHeap) Less(i, j int) bool { return timers[i].t < timers[j].t } func (timerHeap) Swap(i, j int) { timers[i], timers[j] = timers[j], timers[i] timers[i].i = i timers[j].i = j } func (timerHeap) Push(x interface{}) { e := x.(*Timer) e.i = len(timers) timers = append(timers, e) } func (timerHeap) Pop() interface{} { // TODO: possibly shrink array. n := len(timers) - 1 e := timers[n] timers[n] = nil timers = timers[0:n] e.i = -1 return e }