From fbb702974760e8c0b066b0e4a2f2f18aeef9204b Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Wed, 23 Sep 2009 13:02:14 -0700 Subject: [PATCH] expand ticker interface to allow a client to shut down a ticker. existing interface still works. R=rsc DELTA=50 (32 added, 2 deleted, 16 changed) OCL=34930 CL=34932 --- src/pkg/time/tick.go | 53 ++++++++++++++++++++++++++++----------- src/pkg/time/tick_test.go | 13 +++++++--- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/pkg/time/tick.go b/src/pkg/time/tick.go index e716ba57a3a..b664077c1b8 100644 --- a/src/pkg/time/tick.go +++ b/src/pkg/time/tick.go @@ -7,7 +7,7 @@ package time // TODO(rsc): This implementation of Tick is a // simple placeholder. Eventually, there will need to be // a single central time server no matter how many tickers -// are active. There also needs to be a way to cancel a ticker. +// are active. // // Also, if timeouts become part of the select statement, // perhaps the Ticker is just: @@ -19,40 +19,63 @@ package time // c <- nsec; // } -func ticker(ns int64, c chan int64) { + +// A Ticker holds a synchronous channel that delivers `ticks' of a clock +// at intervals. +type Ticker struct { + C <-chan int64; // The channel on which the ticks are delivered. + ns int64; + shutdown bool; +} + +// Stop turns off a ticker. After Stop, no more ticks will be delivered. +func (t *Ticker) Stop() { + t.shutdown = true +} + +func (t *Ticker) ticker(c chan<- int64) { now := Nanoseconds(); when := now; - for { - when += ns; // next alarm + for !t.shutdown { + when += t.ns; // next alarm // if c <- now took too long, skip ahead if when < now { // one big step - when += (now-when)/ns * ns; + when += (now-when)/t.ns * t.ns; } for when <= now { // little steps until when > now - when += ns + when += t.ns } Sleep(when - now); now = Nanoseconds(); - c <- now; - if closed(c) { + if t.shutdown { return; } + c <- now; } } -// Tick creates a synchronous channel that will send the time, in nanoseconds, -// every ns nanoseconds. It adjusts the intervals to make up for pauses in -// delivery of the ticks. -func Tick(ns int64) chan int64 { +// Tick is a convenience wrapper for NewTicker providing access to the ticking +// channel only. Useful for clients that have no need to shut down the ticker. +func Tick(ns int64) <-chan int64 { + if ns <= 0 { + return nil + } + return NewTicker(ns).C; +} + +// Ticker returns a new Ticker containing a synchronous channel that will +// send the time, in nanoseconds, every ns nanoseconds. It adjusts the +// intervals to make up for pauses in delivery of the ticks. +func NewTicker(ns int64) *Ticker { if ns <= 0 { return nil } c := make(chan int64); - go ticker(ns, c); - return c; + t := &Ticker{c, ns, false}; + go t.ticker(c); + return t; } - diff --git a/src/pkg/time/tick_test.go b/src/pkg/time/tick_test.go index c7cad1217eb..4a82eee7437 100644 --- a/src/pkg/time/tick_test.go +++ b/src/pkg/time/tick_test.go @@ -9,16 +9,17 @@ import ( . "time"; ) -func TestTick(t *testing.T) { +func TestTicker(t *testing.T) { const ( Delta = 100*1e6; Count = 10; ); - c := Tick(Delta); + ticker := NewTicker(Delta); t0 := Nanoseconds(); for i := 0; i < Count; i++ { - <-c; + <-ticker.C; } + ticker.Stop(); t1 := Nanoseconds(); ns := t1 - t0; target := int64(Delta*Count); @@ -26,4 +27,10 @@ func TestTick(t *testing.T) { if ns < target - slop || ns > target + slop { t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target)); } + // Now test that the ticker stopped + Sleep(2*Delta); + _, received := <-ticker.C; + if received { + t.Fatalf("Ticker did not shut down"); + } }