// 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:
// 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
}
- c := make(chan int64);
- go ticker(ns, c);
- return c;
+ 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);
+ t := &Ticker{c, ns, false};
+ go t.ticker(c);
+ return t;
+}
. "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);
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");
+ }
}