diff options
Diffstat (limited to 'src/pkg/time/sleep.go')
-rw-r--r-- | src/pkg/time/sleep.go | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/pkg/time/sleep.go b/src/pkg/time/sleep.go new file mode 100644 index 000000000..314622d0d --- /dev/null +++ b/src/pkg/time/sleep.go @@ -0,0 +1,177 @@ +// 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 +} |