1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
12 // A Ticker holds a synchronous channel that delivers `ticks' of a clock
15 C <-chan int64 // The channel on which the ticks are delivered.
16 c chan<- int64 // The same channel, but the end we use.
18 shutdown chan bool // Buffered channel used to signal shutdown.
23 // Stop turns off a ticker. After Stop, no more ticks will be sent.
24 func (t *Ticker) Stop() {
26 case t.shutdown <- true:
29 // Stop in progress already
33 // Tick is a convenience wrapper for NewTicker providing access to the ticking
34 // channel only. Useful for clients that have no need to shut down the ticker.
35 func Tick(ns int64) <-chan int64 {
39 return NewTicker(ns).C
43 wakeUp chan bool // wakeup signals sent/received here
48 // Set alarm to go off at time ns, if not already set earlier.
49 func (a *alarmer) set(ns int64) {
52 // Next tick we expect is too late; shut down the late runner
53 // and (after fallthrough) start a new wakeLoop.
56 case a.wakeMeAt == nil:
57 // There's no wakeLoop, start one.
58 a.wakeMeAt = make(chan int64)
59 a.wakeUp = make(chan bool, 1)
60 go wakeLoop(a.wakeMeAt, a.wakeUp)
63 // Nobody else is waiting; it's just us.
67 // There's already someone scheduled.
71 // Channel to notify tickerLoop of new Tickers being created.
72 var newTicker chan *Ticker
74 func startTickerLoop() {
75 newTicker = make(chan *Ticker)
79 // wakeLoop delivers ticks at scheduled times, sleeping until the right moment.
80 // If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new
81 // wakeLoop and signal that this one is done by closing the wakeMeAt channel.
82 func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) {
83 for wakeAt := range wakeMeAt {
84 Sleep(wakeAt - Nanoseconds())
89 // A single tickerLoop serves all ticks to Tickers. It waits for two events:
90 // either the creation of a new Ticker or a tick from the alarm,
91 // signaling a time to wake up one or more Tickers.
93 // Represents the next alarm to be delivered.
95 var now, wakeTime int64
99 case t := <-newTicker:
100 // Add Ticker to list
103 // Arrange for a new alarm if this one precedes the existing one.
104 alarm.set(t.nextTick)
107 wakeTime = now + 1e15 // very long in the future
108 var prev *Ticker = nil
109 // Scan list of tickers, delivering updates to those
110 // that need it and determining the next wake time.
111 // TODO(r): list should be sorted in time order.
112 for t := tickers; t != nil; t = t.next {
115 // Ticker is done; remove it from list.
124 if t.nextTick <= now {
126 // Only send if there's room. We must not block.
127 // The channel is allocated with a one-element
128 // buffer, which is sufficient: if he hasn't picked
129 // up the last tick, no point in sending more.
133 if t.nextTick <= now {
134 // Still behind; advance in one big step.
135 t.nextTick += (now - t.nextTick + t.ns) / t.ns * t.ns
138 if t.nextTick < wakeTime {
139 wakeTime = t.nextTick
144 // Please send wakeup at earliest required time.
145 // If there are no tickers, don't bother.
146 alarm.wakeTime = wakeTime
147 alarm.wakeMeAt <- wakeTime
155 var onceStartTickerLoop sync.Once
157 // NewTicker returns a new Ticker containing a channel that will
158 // send the time, in nanoseconds, every ns nanoseconds. It adjusts the
159 // intervals to make up for pauses in delivery of the ticks. The value of
160 // ns must be greater than zero; if not, NewTicker will panic.
161 func NewTicker(ns int64) *Ticker {
163 panic(os.NewError("non-positive interval for NewTicker"))
165 c := make(chan int64, 1) // See comment on send in tickerLoop
170 shutdown: make(chan bool, 1),
171 nextTick: Nanoseconds() + ns,
173 onceStartTickerLoop.Do(startTickerLoop)
174 // must be run in background so global Tickers can be created
175 go func() { newTicker <- t }()