// 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 := getg().timer if t == nil { t = new(timer) getg().timer = t } *t = 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 poll_runtimeNano internal/poll.runtimeNano func poll_runtimeNano() int64 { return nanotime() } //go:linkname time_runtimeNano time.runtimeNano func time_runtimeNano() int64 { return nanotime() } var startNano int64 = nanotime()