Commit 0c02bd18 authored by Eoghan Sherry's avatar Eoghan Sherry Committed by Rob Pike

time: fix tick accuracy when using multiple Tickers

* correctly ignore obsolete ticks
* fix update of next alarm time
* tighten alarm channel buffers
Fixes #1379.

R=r, adg, r2
CC=golang-dev, soul9
https://golang.org/cl/3789045
parent c6ed78a4
...@@ -47,11 +47,12 @@ func (a *alarmer) set(ns int64) { ...@@ -47,11 +47,12 @@ func (a *alarmer) set(ns int64) {
case a.wakeTime > ns: case a.wakeTime > ns:
// Next tick we expect is too late; shut down the late runner // Next tick we expect is too late; shut down the late runner
// and (after fallthrough) start a new wakeLoop. // and (after fallthrough) start a new wakeLoop.
a.wakeMeAt <- -1 close(a.wakeMeAt)
fallthrough fallthrough
case a.wakeMeAt == nil: case a.wakeMeAt == nil:
// There's no wakeLoop, start one. // There's no wakeLoop, start one.
a.wakeMeAt = make(chan int64, 10) a.wakeMeAt = make(chan int64)
a.wakeUp = make(chan bool, 1)
go wakeLoop(a.wakeMeAt, a.wakeUp) go wakeLoop(a.wakeMeAt, a.wakeUp)
fallthrough fallthrough
case a.wakeTime == 0: case a.wakeTime == 0:
...@@ -73,19 +74,10 @@ func startTickerLoop() { ...@@ -73,19 +74,10 @@ func startTickerLoop() {
// wakeLoop delivers ticks at scheduled times, sleeping until the right moment. // wakeLoop delivers ticks at scheduled times, sleeping until the right moment.
// If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new // If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new
// wakeLoop but they will share the wakeUp channel and signal that this one // wakeLoop and signal that this one is done by closing the wakeMeAt channel.
// is done by giving it a negative time request.
func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) { func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) {
for { for wakeAt := range wakeMeAt {
wakeAt := <-wakeMeAt Sleep(wakeAt - Nanoseconds())
if wakeAt < 0 { // tickerLoop has started another wakeLoop
return
}
now := Nanoseconds()
if wakeAt > now {
Sleep(wakeAt - now)
now = Nanoseconds()
}
wakeUp <- true wakeUp <- true
} }
} }
...@@ -96,9 +88,7 @@ func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) { ...@@ -96,9 +88,7 @@ func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) {
func tickerLoop() { func tickerLoop() {
// Represents the next alarm to be delivered. // Represents the next alarm to be delivered.
var alarm alarmer var alarm alarmer
// All wakeLoops deliver wakeups to this channel. var now, wakeTime int64
alarm.wakeUp = make(chan bool, 10)
var now, prevTime, wakeTime int64
var tickers *Ticker var tickers *Ticker
for { for {
select { select {
...@@ -110,10 +100,6 @@ func tickerLoop() { ...@@ -110,10 +100,6 @@ func tickerLoop() {
alarm.set(t.nextTick) alarm.set(t.nextTick)
case <-alarm.wakeUp: case <-alarm.wakeUp:
now = Nanoseconds() now = Nanoseconds()
// Ignore an old time due to a dying wakeLoop
if now < prevTime {
continue
}
wakeTime = now + 1e15 // very long in the future wakeTime = now + 1e15 // very long in the future
var prev *Ticker = nil var prev *Ticker = nil
// Scan list of tickers, delivering updates to those // Scan list of tickers, delivering updates to those
...@@ -151,12 +137,12 @@ func tickerLoop() { ...@@ -151,12 +137,12 @@ func tickerLoop() {
if tickers != nil { if tickers != nil {
// Please send wakeup at earliest required time. // Please send wakeup at earliest required time.
// If there are no tickers, don't bother. // If there are no tickers, don't bother.
alarm.wakeTime = wakeTime
alarm.wakeMeAt <- wakeTime alarm.wakeMeAt <- wakeTime
} else { } else {
alarm.wakeTime = 0 alarm.wakeTime = 0
} }
} }
prevTime = now
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment