Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1)

Delta Between Two Patch Sets: src/pkg/time/tick.go

Issue 5334051: code review 5334051: runtime: add timer support, use for package time (Closed)
Left Patch Set: Created 13 years, 5 months ago
Right Patch Set: diff -r 9870fbad1533 https://go.googlecode.com/hg Created 13 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/time/sys.go ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 // Copyright 2009 The Go Authors. All rights reserved. 1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package time 5 package time
6 6
7 import ( 7 import "errors"
8 » "errors"
9 » "sync"
10 )
11 8
12 // A Ticker holds a synchronous channel that delivers `ticks' of a clock 9 // A Ticker holds a synchronous channel that delivers `ticks' of a clock
13 // at intervals. 10 // at intervals.
14 type Ticker struct { 11 type Ticker struct {
15 » C <-chan int64 // The channel on which the ticks are delivered. 12 » C <-chan int64 // The channel on which the ticks are delivered.
16 » c chan<- int64 // The same channel, but the end we use. 13 » r runtimeTimer
17 » ns int64 14 }
18 » shutdown chan bool // Buffered channel used to signal shutdown. 15
19 » nextTick int64 16 // NewTicker returns a new Ticker containing a channel that will
20 » next *Ticker 17 // send the time, in nanoseconds, every ns nanoseconds. It adjusts the
18 // intervals to make up for pauses in delivery of the ticks. The value of
19 // ns must be greater than zero; if not, NewTicker will panic.
20 func NewTicker(ns int64) *Ticker {
21 » if ns <= 0 {
22 » » panic(errors.New("non-positive interval for NewTicker"))
23 » }
24 » // Give the channel a 1-element time buffer.
25 » // If the client falls behind while reading, we drop ticks
26 » // on the floor until the client catches up.
27 » c := make(chan int64, 1)
28 » t := &Ticker{
29 » » C: c,
30 » » r: runtimeTimer{
31 » » » when: Nanoseconds() + ns,
32 » » » period: ns,
33 » » » f: sendTime,
34 » » » arg: c,
35 » » },
36 » }
37 » startTimer(&t.r)
38 » return t
21 } 39 }
22 40
23 // Stop turns off a ticker. After Stop, no more ticks will be sent. 41 // Stop turns off a ticker. After Stop, no more ticks will be sent.
24 func (t *Ticker) Stop() { 42 func (t *Ticker) Stop() {
25 » select { 43 » stopTimer(&t.r)
26 » case t.shutdown <- true:
27 » » // ok
28 » default:
29 » » // Stop in progress already
30 » }
31 } 44 }
32 45
33 // Tick is a convenience wrapper for NewTicker providing access to the ticking 46 // 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. 47 // channel only. Useful for clients that have no need to shut down the ticker.
35 func Tick(ns int64) <-chan int64 { 48 func Tick(ns int64) <-chan int64 {
36 if ns <= 0 { 49 if ns <= 0 {
37 return nil 50 return nil
38 } 51 }
39 return NewTicker(ns).C 52 return NewTicker(ns).C
40 } 53 }
41
42 type alarmer struct {
43 wakeUp chan bool // wakeup signals sent/received here
44 wakeMeAt chan int64
45 wakeTime int64
46 }
47
48 // Set alarm to go off at time ns, if not already set earlier.
49 func (a *alarmer) set(ns int64) {
50 switch {
51 case a.wakeTime > ns:
52 // Next tick we expect is too late; shut down the late runner
53 // and (after fallthrough) start a new wakeLoop.
54 close(a.wakeMeAt)
55 fallthrough
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)
61 fallthrough
62 case a.wakeTime == 0:
63 // Nobody else is waiting; it's just us.
64 a.wakeTime = ns
65 a.wakeMeAt <- ns
66 default:
67 // There's already someone scheduled.
68 }
69 }
70
71 // Channel to notify tickerLoop of new Tickers being created.
72 var newTicker chan *Ticker
73
74 func startTickerLoop() {
75 newTicker = make(chan *Ticker)
76 go tickerLoop()
77 }
78
79 // wakeLoop delivers ticks at scheduled times, sleeping until the right moment.
80 // If another, earlier Ticker is created while it sleeps, tickerLoop() will star t 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())
85 wakeUp <- true
86 }
87 }
88
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.
92 func tickerLoop() {
93 // Represents the next alarm to be delivered.
94 var alarm alarmer
95 var now, wakeTime int64
96 var tickers *Ticker
97 for {
98 select {
99 case t := <-newTicker:
100 // Add Ticker to list
101 t.next = tickers
102 tickers = t
103 // Arrange for a new alarm if this one precedes the exis ting one.
104 alarm.set(t.nextTick)
105 case <-alarm.wakeUp:
106 now = Nanoseconds()
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 {
113 select {
114 case <-t.shutdown:
115 // Ticker is done; remove it from list.
116 if prev == nil {
117 tickers = t.next
118 } else {
119 prev.next = t.next
120 }
121 continue
122 default:
123 }
124 if t.nextTick <= now {
125 if len(t.c) == 0 {
126 // Only send if there's room. W e 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.
130 t.c <- now
131 }
132 t.nextTick += t.ns
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
136 }
137 }
138 if t.nextTick < wakeTime {
139 wakeTime = t.nextTick
140 }
141 prev = t
142 }
143 if tickers != nil {
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
148 } else {
149 alarm.wakeTime = 0
150 }
151 }
152 }
153 }
154
155 var onceStartTickerLoop sync.Once
156
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 {
162 if ns <= 0 {
163 panic(errors.New("non-positive interval for NewTicker"))
164 }
165 c := make(chan int64, 1) // See comment on send in tickerLoop
166 t := &Ticker{
167 C: c,
168 c: c,
169 ns: ns,
170 shutdown: make(chan bool, 1),
171 nextTick: Nanoseconds() + ns,
172 }
173 onceStartTickerLoop.Do(startTickerLoop)
174 // must be run in background so global Tickers can be created
175 go func() { newTicker <- t }()
176 return t
177 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b