Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 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. |
| 4 | |
| 5 | // Time-related runtime and pieces of package time. |
| 6 | |
| 7 | package runtime |
| 8 | |
| 9 | import "unsafe" |
| 10 | |
| 11 | // Package time knows the layout of this structure. |
Dmitriy Vyukov | 91a670d | 2014-09-04 10:04:04 +0400 | [diff] [blame] | 12 | // If this struct changes, adjust ../time/sleep.go:/runtimeTimer. |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 13 | // For GOOS=nacl, package syscall knows the layout of this structure. |
| 14 | // If this struct changes, adjust ../syscall/net_nacl.go:/runtimeTimer. |
| 15 | type timer struct { |
| 16 | i int // heap index |
| 17 | |
| 18 | // Timer wakes up at when, and then at when+period, ... (period > 0 only) |
| 19 | // each time calling f(now, arg) in the timer goroutine, so f must be |
| 20 | // a well-behaved function and not block. |
| 21 | when int64 |
| 22 | period int64 |
Dmitriy Vyukov | 91a670d | 2014-09-04 10:04:04 +0400 | [diff] [blame] | 23 | f func(interface{}, uintptr) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 24 | arg interface{} |
Dmitriy Vyukov | 91a670d | 2014-09-04 10:04:04 +0400 | [diff] [blame] | 25 | seq uintptr |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 26 | } |
| 27 | |
| 28 | var timers struct { |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 29 | lock mutex |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 30 | gp *g |
| 31 | created bool |
| 32 | sleeping bool |
| 33 | rescheduling bool |
| 34 | waitnote note |
| 35 | t []*timer |
| 36 | } |
| 37 | |
Shenghou Ma | 2fe9482 | 2014-10-27 20:35:15 -0400 | [diff] [blame] | 38 | // nacl fake time support - time in nanoseconds since 1970 |
| 39 | var faketime int64 |
Dmitriy Vyukov | 21a4bde | 2014-08-25 23:24:18 +0400 | [diff] [blame] | 40 | |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 41 | // Package time APIs. |
| 42 | // Godoc uses the comments in package time, not these. |
| 43 | |
| 44 | // time.now is implemented in assembly. |
| 45 | |
Russ Cox | 7a524a1 | 2014-12-22 13:27:53 -0500 | [diff] [blame] | 46 | // timeSleep puts the current goroutine to sleep for at least ns nanoseconds. |
| 47 | //go:linkname timeSleep time.Sleep |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 48 | func timeSleep(ns int64) { |
| 49 | if ns <= 0 { |
| 50 | return |
| 51 | } |
| 52 | |
| 53 | t := new(timer) |
Russ Cox | d21638b | 2014-08-27 21:59:49 -0400 | [diff] [blame] | 54 | t.when = nanotime() + ns |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 55 | t.f = goroutineReady |
| 56 | t.arg = getg() |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 57 | lock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 58 | addtimerLocked(t) |
Dmitry Vyukov | 919fd24 | 2015-02-21 21:01:40 +0300 | [diff] [blame] | 59 | goparkunlock(&timers.lock, "sleep", traceEvGoSleep, 2) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | // startTimer adds t to the timer heap. |
Russ Cox | 7a524a1 | 2014-12-22 13:27:53 -0500 | [diff] [blame] | 63 | //go:linkname startTimer time.startTimer |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 64 | func startTimer(t *timer) { |
| 65 | if raceenabled { |
| 66 | racerelease(unsafe.Pointer(t)) |
| 67 | } |
| 68 | addtimer(t) |
| 69 | } |
| 70 | |
| 71 | // stopTimer removes t from the timer heap if it is there. |
| 72 | // It returns true if t was removed, false if t wasn't even there. |
Russ Cox | 7a524a1 | 2014-12-22 13:27:53 -0500 | [diff] [blame] | 73 | //go:linkname stopTimer time.stopTimer |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 74 | func stopTimer(t *timer) bool { |
| 75 | return deltimer(t) |
| 76 | } |
| 77 | |
| 78 | // Go runtime. |
| 79 | |
| 80 | // Ready the goroutine arg. |
Dmitriy Vyukov | 91a670d | 2014-09-04 10:04:04 +0400 | [diff] [blame] | 81 | func goroutineReady(arg interface{}, seq uintptr) { |
Dmitry Vyukov | 919fd24 | 2015-02-21 21:01:40 +0300 | [diff] [blame] | 82 | goready(arg.(*g), 0) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 83 | } |
| 84 | |
| 85 | func addtimer(t *timer) { |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 86 | lock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 87 | addtimerLocked(t) |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 88 | unlock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | // Add a timer to the heap and start or kick the timer proc. |
| 92 | // If the new timer is earlier than any of the others. |
| 93 | // Timers are locked. |
| 94 | func addtimerLocked(t *timer) { |
| 95 | // when must never be negative; otherwise timerproc will overflow |
| 96 | // during its delta calculation and never expire other runtime·timers. |
| 97 | if t.when < 0 { |
| 98 | t.when = 1<<63 - 1 |
| 99 | } |
| 100 | t.i = len(timers.t) |
| 101 | timers.t = append(timers.t, t) |
| 102 | siftupTimer(t.i) |
| 103 | if t.i == 0 { |
| 104 | // siftup moved to top: new earliest deadline. |
| 105 | if timers.sleeping { |
| 106 | timers.sleeping = false |
Russ Cox | d21638b | 2014-08-27 21:59:49 -0400 | [diff] [blame] | 107 | notewakeup(&timers.waitnote) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 108 | } |
| 109 | if timers.rescheduling { |
| 110 | timers.rescheduling = false |
Dmitry Vyukov | 919fd24 | 2015-02-21 21:01:40 +0300 | [diff] [blame] | 111 | goready(timers.gp, 0) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 112 | } |
| 113 | } |
| 114 | if !timers.created { |
| 115 | timers.created = true |
| 116 | go timerproc() |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | // Delete timer t from the heap. |
| 121 | // Do not need to update the timerproc: if it wakes up early, no big deal. |
| 122 | func deltimer(t *timer) bool { |
| 123 | // Dereference t so that any panic happens before the lock is held. |
| 124 | // Discard result, because t might be moving in the heap. |
| 125 | _ = t.i |
| 126 | |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 127 | lock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 128 | // t may not be registered anymore and may have |
| 129 | // a bogus i (typically 0, if generated by Go). |
| 130 | // Verify it before proceeding. |
| 131 | i := t.i |
| 132 | last := len(timers.t) - 1 |
| 133 | if i < 0 || i > last || timers.t[i] != t { |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 134 | unlock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 135 | return false |
| 136 | } |
| 137 | if i != last { |
| 138 | timers.t[i] = timers.t[last] |
| 139 | timers.t[i].i = i |
| 140 | } |
| 141 | timers.t[last] = nil |
| 142 | timers.t = timers.t[:last] |
| 143 | if i != last { |
| 144 | siftupTimer(i) |
| 145 | siftdownTimer(i) |
| 146 | } |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 147 | unlock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 148 | return true |
| 149 | } |
| 150 | |
| 151 | // Timerproc runs the time-driven events. |
| 152 | // It sleeps until the next event in the timers heap. |
| 153 | // If addtimer inserts a new earlier event, addtimer1 wakes timerproc early. |
| 154 | func timerproc() { |
| 155 | timers.gp = getg() |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 156 | for { |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 157 | lock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 158 | timers.sleeping = false |
Russ Cox | d21638b | 2014-08-27 21:59:49 -0400 | [diff] [blame] | 159 | now := nanotime() |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 160 | delta := int64(-1) |
| 161 | for { |
| 162 | if len(timers.t) == 0 { |
| 163 | delta = -1 |
| 164 | break |
| 165 | } |
| 166 | t := timers.t[0] |
| 167 | delta = t.when - now |
| 168 | if delta > 0 { |
| 169 | break |
| 170 | } |
| 171 | if t.period > 0 { |
| 172 | // leave in heap but adjust next time to fire |
| 173 | t.when += t.period * (1 + -delta/t.period) |
| 174 | siftdownTimer(0) |
| 175 | } else { |
| 176 | // remove from heap |
| 177 | last := len(timers.t) - 1 |
| 178 | if last > 0 { |
| 179 | timers.t[0] = timers.t[last] |
| 180 | timers.t[0].i = 0 |
| 181 | } |
| 182 | timers.t[last] = nil |
| 183 | timers.t = timers.t[:last] |
| 184 | if last > 0 { |
| 185 | siftdownTimer(0) |
| 186 | } |
| 187 | t.i = -1 // mark as removed |
| 188 | } |
| 189 | f := t.f |
| 190 | arg := t.arg |
Dmitriy Vyukov | 91a670d | 2014-09-04 10:04:04 +0400 | [diff] [blame] | 191 | seq := t.seq |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 192 | unlock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 193 | if raceenabled { |
| 194 | raceacquire(unsafe.Pointer(t)) |
| 195 | } |
Dmitriy Vyukov | 91a670d | 2014-09-04 10:04:04 +0400 | [diff] [blame] | 196 | f(arg, seq) |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 197 | lock(&timers.lock) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 198 | } |
Shenghou Ma | 2fe9482 | 2014-10-27 20:35:15 -0400 | [diff] [blame] | 199 | if delta < 0 || faketime > 0 { |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 200 | // No timers left - put goroutine to sleep. |
| 201 | timers.rescheduling = true |
Dmitry Vyukov | 919fd24 | 2015-02-21 21:01:40 +0300 | [diff] [blame] | 202 | goparkunlock(&timers.lock, "timer goroutine (idle)", traceEvGoBlock, 1) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 203 | continue |
| 204 | } |
| 205 | // At least one timer pending. Sleep until then. |
| 206 | timers.sleeping = true |
Russ Cox | d21638b | 2014-08-27 21:59:49 -0400 | [diff] [blame] | 207 | noteclear(&timers.waitnote) |
Russ Cox | 8ecb9a7 | 2014-08-27 23:32:49 -0400 | [diff] [blame] | 208 | unlock(&timers.lock) |
Russ Cox | d21638b | 2014-08-27 21:59:49 -0400 | [diff] [blame] | 209 | notetsleepg(&timers.waitnote, delta) |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 210 | } |
| 211 | } |
| 212 | |
Shenghou Ma | 2fe9482 | 2014-10-27 20:35:15 -0400 | [diff] [blame] | 213 | func timejump() *g { |
| 214 | if faketime == 0 { |
| 215 | return nil |
| 216 | } |
| 217 | |
| 218 | lock(&timers.lock) |
| 219 | if !timers.created || len(timers.t) == 0 { |
| 220 | unlock(&timers.lock) |
| 221 | return nil |
| 222 | } |
| 223 | |
| 224 | var gp *g |
| 225 | if faketime < timers.t[0].when { |
| 226 | faketime = timers.t[0].when |
| 227 | if timers.rescheduling { |
| 228 | timers.rescheduling = false |
| 229 | gp = timers.gp |
| 230 | } |
| 231 | } |
| 232 | unlock(&timers.lock) |
| 233 | return gp |
| 234 | } |
| 235 | |
Dmitriy Vyukov | 9601aba | 2014-08-25 20:25:22 +0400 | [diff] [blame] | 236 | // Heap maintenance algorithms. |
| 237 | |
| 238 | func siftupTimer(i int) { |
| 239 | t := timers.t |
| 240 | when := t[i].when |
| 241 | tmp := t[i] |
| 242 | for i > 0 { |
| 243 | p := (i - 1) / 4 // parent |
| 244 | if when >= t[p].when { |
| 245 | break |
| 246 | } |
| 247 | t[i] = t[p] |
| 248 | t[i].i = i |
| 249 | t[p] = tmp |
| 250 | t[p].i = p |
| 251 | i = p |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | func siftdownTimer(i int) { |
| 256 | t := timers.t |
| 257 | n := len(t) |
| 258 | when := t[i].when |
| 259 | tmp := t[i] |
| 260 | for { |
| 261 | c := i*4 + 1 // left child |
| 262 | c3 := c + 2 // mid child |
| 263 | if c >= n { |
| 264 | break |
| 265 | } |
| 266 | w := t[c].when |
| 267 | if c+1 < n && t[c+1].when < w { |
| 268 | w = t[c+1].when |
| 269 | c++ |
| 270 | } |
| 271 | if c3 < n { |
| 272 | w3 := t[c3].when |
| 273 | if c3+1 < n && t[c3+1].when < w3 { |
| 274 | w3 = t[c3+1].when |
| 275 | c3++ |
| 276 | } |
| 277 | if w3 < w { |
| 278 | w = w3 |
| 279 | c = c3 |
| 280 | } |
| 281 | } |
| 282 | if w >= when { |
| 283 | break |
| 284 | } |
| 285 | t[i] = t[c] |
| 286 | t[i].i = i |
| 287 | t[c] = tmp |
| 288 | t[c].i = c |
| 289 | i = c |
| 290 | } |
| 291 | } |
Russ Cox | 7a524a1 | 2014-12-22 13:27:53 -0500 | [diff] [blame] | 292 | |
| 293 | // Entry points for net, time to call nanotime. |
| 294 | |
| 295 | //go:linkname net_runtimeNano net.runtimeNano |
| 296 | func net_runtimeNano() int64 { |
| 297 | return nanotime() |
| 298 | } |
| 299 | |
| 300 | //go:linkname time_runtimeNano time.runtimeNano |
| 301 | func time_runtimeNano() int64 { |
| 302 | return nanotime() |
| 303 | } |