| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Time-related runtime and pieces of package time. |
| |
| package runtime |
| |
| import "unsafe" |
| |
| // Package time knows the layout of this structure. |
| // If this struct changes, adjust ../time/sleep.go:/runtimeTimer. |
| // For GOOS=nacl, package syscall knows the layout of this structure. |
| // If this struct changes, adjust ../syscall/net_nacl.go:/runtimeTimer. |
| type timer struct { |
| i int // heap index |
| |
| // Timer wakes up at when, and then at when+period, ... (period > 0 only) |
| // each time calling f(arg, now) in the timer goroutine, so f must be |
| // a well-behaved function and not block. |
| when int64 |
| period int64 |
| f func(interface{}, uintptr) |
| arg interface{} |
| seq uintptr |
| } |
| |
| var timers struct { |
| lock mutex |
| gp *g |
| created bool |
| sleeping bool |
| rescheduling bool |
| waitnote note |
| t []*timer |
| } |
| |
| // nacl fake time support - time in nanoseconds since 1970 |
| var faketime int64 |
| |
| // Package time APIs. |
| // Godoc uses the comments in package time, not these. |
| |
| // time.now is implemented in assembly. |
| |
| // timeSleep puts the current goroutine to sleep for at least ns nanoseconds. |
| //go:linkname timeSleep time.Sleep |
| func timeSleep(ns int64) { |
| if ns <= 0 { |
| return |
| } |
| |
| t := new(timer) |
| t.when = nanotime() + ns |
| t.f = goroutineReady |
| t.arg = getg() |
| lock(&timers.lock) |
| addtimerLocked(t) |
| goparkunlock(&timers.lock, "sleep", traceEvGoSleep, 2) |
| } |
| |
| // startTimer adds t to the timer heap. |
| //go:linkname startTimer time.startTimer |
| func startTimer(t *timer) { |
| if raceenabled { |
| racerelease(unsafe.Pointer(t)) |
| } |
| addtimer(t) |
| } |
| |
| // stopTimer removes t from the timer heap if it is there. |
| // It returns true if t was removed, false if t wasn't even there. |
| //go:linkname stopTimer time.stopTimer |
| func stopTimer(t *timer) bool { |
| return deltimer(t) |
| } |
| |
| // Go runtime. |
| |
| // Ready the goroutine arg. |
| func goroutineReady(arg interface{}, seq uintptr) { |
| goready(arg.(*g), 0) |
| } |
| |
| func addtimer(t *timer) { |
| lock(&timers.lock) |
| addtimerLocked(t) |
| unlock(&timers.lock) |
| } |
| |
| // Add a timer to the heap and start or kick timerproc if the new timer is |
| // earlier than any of the others. |
| // Timers are locked. |
| func addtimerLocked(t *timer) { |
| // when must never be negative; otherwise timerproc will overflow |
| // during its delta calculation and never expire other runtime timers. |
| if t.when < 0 { |
| t.when = 1<<63 - 1 |
| } |
| t.i = len(timers.t) |
| timers.t = append(timers.t, t) |
| siftupTimer(t.i) |
| if t.i == 0 { |
| // siftup moved to top: new earliest deadline. |
| if timers.sleeping { |
| timers.sleeping = false |
| notewakeup(&timers.waitnote) |
| } |
| if timers.rescheduling { |
| timers.rescheduling = false |
| goready(timers.gp, 0) |
| } |
| } |
| if !timers.created { |
| timers.created = true |
| go timerproc() |
| } |
| } |
| |
| // Delete timer t from the heap. |
| // Do not need to update the timerproc: if it wakes up early, no big deal. |
| func deltimer(t *timer) bool { |
| // Dereference t so that any panic happens before the lock is held. |
| // Discard result, because t might be moving in the heap. |
| _ = t.i |
| |
| lock(&timers.lock) |
| // t may not be registered anymore and may have |
| // a bogus i (typically 0, if generated by Go). |
| // Verify it before proceeding. |
| i := t.i |
| last := len(timers.t) - 1 |
| if i < 0 || i > last || timers.t[i] != t { |
| unlock(&timers.lock) |
| return false |
| } |
| if i != last { |
| timers.t[i] = timers.t[last] |
| timers.t[i].i = i |
| } |
| timers.t[last] = nil |
| timers.t = timers.t[:last] |
| if i != last { |
| siftupTimer(i) |
| siftdownTimer(i) |
| } |
| unlock(&timers.lock) |
| return true |
| } |
| |
| // Timerproc runs the time-driven events. |
| // It sleeps until the next event in the timers heap. |
| // If addtimer inserts a new earlier event, it wakes timerproc early. |
| func timerproc() { |
| timers.gp = getg() |
| for { |
| lock(&timers.lock) |
| timers.sleeping = false |
| now := nanotime() |
| delta := int64(-1) |
| for { |
| if len(timers.t) == 0 { |
| delta = -1 |
| break |
| } |
| t := timers.t[0] |
| delta = t.when - now |
| if delta > 0 { |
| break |
| } |
| if t.period > 0 { |
| // leave in heap but adjust next time to fire |
| t.when += t.period * (1 + -delta/t.period) |
| siftdownTimer(0) |
| } else { |
| // remove from heap |
| last := len(timers.t) - 1 |
| if last > 0 { |
| timers.t[0] = timers.t[last] |
| timers.t[0].i = 0 |
| } |
| timers.t[last] = nil |
| timers.t = timers.t[:last] |
| if last > 0 { |
| siftdownTimer(0) |
| } |
| t.i = -1 // mark as removed |
| } |
| f := t.f |
| arg := t.arg |
| seq := t.seq |
| unlock(&timers.lock) |
| if raceenabled { |
| raceacquire(unsafe.Pointer(t)) |
| } |
| f(arg, seq) |
| lock(&timers.lock) |
| } |
| if delta < 0 || faketime > 0 { |
| // No timers left - put goroutine to sleep. |
| timers.rescheduling = true |
| goparkunlock(&timers.lock, "timer goroutine (idle)", traceEvGoBlock, 1) |
| continue |
| } |
| // At least one timer pending. Sleep until then. |
| timers.sleeping = true |
| noteclear(&timers.waitnote) |
| unlock(&timers.lock) |
| notetsleepg(&timers.waitnote, delta) |
| } |
| } |
| |
| func timejump() *g { |
| if faketime == 0 { |
| return nil |
| } |
| |
| lock(&timers.lock) |
| if !timers.created || len(timers.t) == 0 { |
| unlock(&timers.lock) |
| return nil |
| } |
| |
| var gp *g |
| if faketime < timers.t[0].when { |
| faketime = timers.t[0].when |
| if timers.rescheduling { |
| timers.rescheduling = false |
| gp = timers.gp |
| } |
| } |
| unlock(&timers.lock) |
| return gp |
| } |
| |
| // Heap maintenance algorithms. |
| |
| func siftupTimer(i int) { |
| t := timers.t |
| when := t[i].when |
| tmp := t[i] |
| for i > 0 { |
| p := (i - 1) / 4 // parent |
| if when >= t[p].when { |
| break |
| } |
| t[i] = t[p] |
| t[i].i = i |
| t[p] = tmp |
| t[p].i = p |
| i = p |
| } |
| } |
| |
| func siftdownTimer(i int) { |
| t := timers.t |
| n := len(t) |
| when := t[i].when |
| tmp := t[i] |
| for { |
| c := i*4 + 1 // left child |
| c3 := c + 2 // mid child |
| if c >= n { |
| break |
| } |
| w := t[c].when |
| if c+1 < n && t[c+1].when < w { |
| w = t[c+1].when |
| c++ |
| } |
| if c3 < n { |
| w3 := t[c3].when |
| if c3+1 < n && t[c3+1].when < w3 { |
| w3 = t[c3+1].when |
| c3++ |
| } |
| if w3 < w { |
| w = w3 |
| c = c3 |
| } |
| } |
| if w >= when { |
| break |
| } |
| t[i] = t[c] |
| t[i].i = i |
| t[c] = tmp |
| t[c].i = c |
| i = c |
| } |
| } |
| |
| // Entry points for net, time to call nanotime. |
| |
| //go:linkname net_runtimeNano net.runtimeNano |
| func net_runtimeNano() int64 { |
| return nanotime() |
| } |
| |
| //go:linkname time_runtimeNano time.runtimeNano |
| func time_runtimeNano() int64 { |
| return nanotime() |
| } |