mirror of
https://github.com/golang/go
synced 2024-11-17 05:54:46 -07:00
runtime, time: remove old timer code
Updates #6239 Updates #27707 Change-Id: I65e6471829c9de4677d3ac78ef6cd7aa0a1fc4cb Reviewed-on: https://go-review.googlesource.com/c/go/+/171884 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
This commit is contained in:
parent
8cf5293caa
commit
580337e268
@ -4410,44 +4410,23 @@ func checkdead() {
|
||||
}
|
||||
|
||||
// Maybe jump time forward for playground.
|
||||
if oldTimers {
|
||||
gp := timejumpOld()
|
||||
if gp != nil {
|
||||
casgstatus(gp, _Gwaiting, _Grunnable)
|
||||
globrunqput(gp)
|
||||
_p_ := pidleget()
|
||||
if _p_ == nil {
|
||||
throw("checkdead: no p for timer")
|
||||
_p_ := timejump()
|
||||
if _p_ != nil {
|
||||
for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link {
|
||||
if (*pp).ptr() == _p_ {
|
||||
*pp = _p_.link
|
||||
break
|
||||
}
|
||||
mp := mget()
|
||||
if mp == nil {
|
||||
// There should always be a free M since
|
||||
// nothing is running.
|
||||
throw("checkdead: no m for timer")
|
||||
}
|
||||
mp.nextp.set(_p_)
|
||||
notewakeup(&mp.park)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_p_ := timejump()
|
||||
if _p_ != nil {
|
||||
for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link {
|
||||
if (*pp).ptr() == _p_ {
|
||||
*pp = _p_.link
|
||||
break
|
||||
}
|
||||
}
|
||||
mp := mget()
|
||||
if mp == nil {
|
||||
// There should always be a free M since
|
||||
// nothing is running.
|
||||
throw("checkdead: no m for timer")
|
||||
}
|
||||
mp.nextp.set(_p_)
|
||||
notewakeup(&mp.park)
|
||||
return
|
||||
mp := mget()
|
||||
if mp == nil {
|
||||
// There should always be a free M since
|
||||
// nothing is running.
|
||||
throw("checkdead: no m for timer")
|
||||
}
|
||||
mp.nextp.set(_p_)
|
||||
notewakeup(&mp.park)
|
||||
return
|
||||
}
|
||||
|
||||
// There are no goroutines running, so we can look at the P's.
|
||||
|
@ -7,24 +7,17 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"internal/cpu"
|
||||
"runtime/internal/atomic"
|
||||
"runtime/internal/sys"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Temporary scaffolding while the new timer code is added.
|
||||
const oldTimers = false
|
||||
|
||||
// Package time knows the layout of this structure.
|
||||
// If this struct changes, adjust ../time/sleep.go:/runtimeTimer.
|
||||
type timer struct {
|
||||
tb *timersBucket // the bucket the timer lives in (oldTimers)
|
||||
i int // heap index (oldTimers)
|
||||
|
||||
// If this timer is on a heap, which P's heap it is on.
|
||||
// puintptr rather than *p to match uintptr in the versions
|
||||
// of this struct defined in other packages. (!oldTimers)
|
||||
// of this struct defined in other packages.
|
||||
pp puintptr
|
||||
|
||||
// Timer wakes up at when, and then at when+period, ... (period > 0 only)
|
||||
@ -36,55 +29,13 @@ type timer struct {
|
||||
arg interface{}
|
||||
seq uintptr
|
||||
|
||||
// What to set the when field to in timerModifiedXX status. (!oldTimers)
|
||||
// What to set the when field to in timerModifiedXX status.
|
||||
nextwhen int64
|
||||
|
||||
// The status field holds one of the values below. (!oldTimers)
|
||||
// The status field holds one of the values below.
|
||||
status uint32
|
||||
}
|
||||
|
||||
// timersLen is the length of timers array.
|
||||
//
|
||||
// Ideally, this would be set to GOMAXPROCS, but that would require
|
||||
// dynamic reallocation
|
||||
//
|
||||
// The current value is a compromise between memory usage and performance
|
||||
// that should cover the majority of GOMAXPROCS values used in the wild.
|
||||
const timersLen = 64
|
||||
|
||||
// timers contains "per-P" timer heaps.
|
||||
//
|
||||
// Timers are queued into timersBucket associated with the current P,
|
||||
// so each P may work with its own timers independently of other P instances.
|
||||
//
|
||||
// Each timersBucket may be associated with multiple P
|
||||
// if GOMAXPROCS > timersLen.
|
||||
var timers [timersLen]struct {
|
||||
timersBucket
|
||||
|
||||
// The padding should eliminate false sharing
|
||||
// between timersBucket values.
|
||||
pad [cpu.CacheLinePadSize - unsafe.Sizeof(timersBucket{})%cpu.CacheLinePadSize]byte
|
||||
}
|
||||
|
||||
func (t *timer) assignBucket() *timersBucket {
|
||||
id := uint8(getg().m.p.ptr().id) % timersLen
|
||||
t.tb = &timers[id].timersBucket
|
||||
return t.tb
|
||||
}
|
||||
|
||||
//go:notinheap
|
||||
type timersBucket struct {
|
||||
lock mutex
|
||||
gp *g
|
||||
created bool
|
||||
sleeping bool
|
||||
rescheduling bool
|
||||
sleepUntil int64
|
||||
waitnote note
|
||||
t []*timer
|
||||
}
|
||||
|
||||
// Code outside this file has to be careful in using a timer value.
|
||||
//
|
||||
// The pp, status, and nextwhen fields may only be used by code in this file.
|
||||
@ -226,11 +177,6 @@ const maxWhen = 1<<63 - 1
|
||||
// timeSleep puts the current goroutine to sleep for at least ns nanoseconds.
|
||||
//go:linkname timeSleep time.Sleep
|
||||
func timeSleep(ns int64) {
|
||||
if oldTimers {
|
||||
timeSleepOld(ns)
|
||||
return
|
||||
}
|
||||
|
||||
if ns <= 0 {
|
||||
return
|
||||
}
|
||||
@ -257,30 +203,6 @@ func resetForSleep(gp *g, ut unsafe.Pointer) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func timeSleepOld(ns int64) {
|
||||
if ns <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
gp := getg()
|
||||
t := gp.timer
|
||||
if t == nil {
|
||||
t = new(timer)
|
||||
gp.timer = t
|
||||
}
|
||||
*t = timer{}
|
||||
t.when = nanotime() + ns
|
||||
t.f = goroutineReady
|
||||
t.arg = gp
|
||||
tb := t.assignBucket()
|
||||
lock(&tb.lock)
|
||||
if !tb.addtimerLocked(t) {
|
||||
unlock(&tb.lock)
|
||||
badTimer()
|
||||
}
|
||||
goparkunlock(&tb.lock, waitReasonSleep, traceEvGoSleep, 3)
|
||||
}
|
||||
|
||||
// startTimer adds t to the timer heap.
|
||||
//go:linkname startTimer time.startTimer
|
||||
func startTimer(t *timer) {
|
||||
@ -318,11 +240,6 @@ func goroutineReady(arg interface{}, seq uintptr) {
|
||||
// That avoids the risk of changing the when field of a timer in some P's heap,
|
||||
// which could cause the heap to become unsorted.
|
||||
func addtimer(t *timer) {
|
||||
if oldTimers {
|
||||
addtimerOld(t)
|
||||
return
|
||||
}
|
||||
|
||||
// when must never be negative; otherwise runtimer will overflow
|
||||
// during its delta calculation and never expire other runtime timers.
|
||||
if t.when < 0 {
|
||||
@ -370,59 +287,11 @@ func doaddtimer(pp *p, t *timer) bool {
|
||||
return siftupTimer(pp.timers, i)
|
||||
}
|
||||
|
||||
func addtimerOld(t *timer) {
|
||||
tb := t.assignBucket()
|
||||
lock(&tb.lock)
|
||||
ok := tb.addtimerLocked(t)
|
||||
unlock(&tb.lock)
|
||||
if !ok {
|
||||
badTimer()
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Returns whether all is well: false if the data structure is corrupt
|
||||
// due to user-level races.
|
||||
func (tb *timersBucket) addtimerLocked(t *timer) bool {
|
||||
// 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(tb.t)
|
||||
tb.t = append(tb.t, t)
|
||||
if !siftupTimer(tb.t, t.i) {
|
||||
return false
|
||||
}
|
||||
if t.i == 0 {
|
||||
// siftup moved to top: new earliest deadline.
|
||||
if tb.sleeping && tb.sleepUntil > t.when {
|
||||
tb.sleeping = false
|
||||
notewakeup(&tb.waitnote)
|
||||
}
|
||||
if tb.rescheduling {
|
||||
tb.rescheduling = false
|
||||
goready(tb.gp, 0)
|
||||
}
|
||||
if !tb.created {
|
||||
tb.created = true
|
||||
go timerproc(tb)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// deltimer deletes the timer t. It may be on some other P, so we can't
|
||||
// actually remove it from the timers heap. We can only mark it as deleted.
|
||||
// It will be removed in due course by the P whose heap it is on.
|
||||
// Reports whether the timer was removed before it was run.
|
||||
func deltimer(t *timer) bool {
|
||||
if oldTimers {
|
||||
return deltimerOld(t)
|
||||
}
|
||||
|
||||
for {
|
||||
switch s := atomic.Load(&t.status); s {
|
||||
case timerWaiting, timerModifiedLater:
|
||||
@ -513,62 +382,9 @@ func dodeltimer0(pp *p) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func deltimerOld(t *timer) bool {
|
||||
if t.tb == nil {
|
||||
// t.tb can be nil if the user created a timer
|
||||
// directly, without invoking startTimer e.g
|
||||
// time.Ticker{C: c}
|
||||
// In this case, return early without any deletion.
|
||||
// See Issue 21874.
|
||||
return false
|
||||
}
|
||||
|
||||
tb := t.tb
|
||||
|
||||
lock(&tb.lock)
|
||||
removed, ok := tb.deltimerLocked(t)
|
||||
unlock(&tb.lock)
|
||||
if !ok {
|
||||
badTimer()
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
func (tb *timersBucket) deltimerLocked(t *timer) (removed, ok bool) {
|
||||
// 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(tb.t) - 1
|
||||
if i < 0 || i > last || tb.t[i] != t {
|
||||
return false, true
|
||||
}
|
||||
if i != last {
|
||||
tb.t[i] = tb.t[last]
|
||||
tb.t[i].i = i
|
||||
}
|
||||
tb.t[last] = nil
|
||||
tb.t = tb.t[:last]
|
||||
ok = true
|
||||
if i != last {
|
||||
if !siftupTimer(tb.t, i) {
|
||||
ok = false
|
||||
}
|
||||
if !siftdownTimer(tb.t, i) {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
return true, ok
|
||||
}
|
||||
|
||||
// modtimer modifies an existing timer.
|
||||
// This is called by the netpoll code.
|
||||
func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) {
|
||||
if oldTimers {
|
||||
modtimerOld(t, when, period, f, arg, seq)
|
||||
return
|
||||
}
|
||||
|
||||
if when < 0 {
|
||||
when = maxWhen
|
||||
}
|
||||
@ -652,35 +468,11 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
func modtimerOld(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) {
|
||||
tb := t.tb
|
||||
|
||||
lock(&tb.lock)
|
||||
_, ok := tb.deltimerLocked(t)
|
||||
if ok {
|
||||
t.when = when
|
||||
t.period = period
|
||||
t.f = f
|
||||
t.arg = arg
|
||||
t.seq = seq
|
||||
ok = tb.addtimerLocked(t)
|
||||
}
|
||||
unlock(&tb.lock)
|
||||
if !ok {
|
||||
badTimer()
|
||||
}
|
||||
}
|
||||
|
||||
// resettimer resets an existing inactive timer to turn it into an active timer,
|
||||
// with a new time for when the timer should fire.
|
||||
// This should be called instead of addtimer if the timer value has been,
|
||||
// or may have been, used previously.
|
||||
func resettimer(t *timer, when int64) {
|
||||
if oldTimers {
|
||||
resettimerOld(t, when)
|
||||
return
|
||||
}
|
||||
|
||||
if when < 0 {
|
||||
when = maxWhen
|
||||
}
|
||||
@ -727,82 +519,6 @@ func resettimer(t *timer, when int64) {
|
||||
}
|
||||
}
|
||||
|
||||
func resettimerOld(t *timer, when int64) {
|
||||
t.when = when
|
||||
addtimer(t)
|
||||
}
|
||||
|
||||
// Timerproc runs the time-driven events.
|
||||
// It sleeps until the next event in the tb heap.
|
||||
// If addtimer inserts a new earlier event, it wakes timerproc early.
|
||||
func timerproc(tb *timersBucket) {
|
||||
tb.gp = getg()
|
||||
for {
|
||||
lock(&tb.lock)
|
||||
tb.sleeping = false
|
||||
now := nanotime()
|
||||
delta := int64(-1)
|
||||
for {
|
||||
if len(tb.t) == 0 {
|
||||
delta = -1
|
||||
break
|
||||
}
|
||||
t := tb.t[0]
|
||||
delta = t.when - now
|
||||
if delta > 0 {
|
||||
break
|
||||
}
|
||||
ok := true
|
||||
if t.period > 0 {
|
||||
// leave in heap but adjust next time to fire
|
||||
t.when += t.period * (1 + -delta/t.period)
|
||||
if !siftdownTimer(tb.t, 0) {
|
||||
ok = false
|
||||
}
|
||||
} else {
|
||||
// remove from heap
|
||||
last := len(tb.t) - 1
|
||||
if last > 0 {
|
||||
tb.t[0] = tb.t[last]
|
||||
tb.t[0].i = 0
|
||||
}
|
||||
tb.t[last] = nil
|
||||
tb.t = tb.t[:last]
|
||||
if last > 0 {
|
||||
if !siftdownTimer(tb.t, 0) {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
t.i = -1 // mark as removed
|
||||
}
|
||||
f := t.f
|
||||
arg := t.arg
|
||||
seq := t.seq
|
||||
unlock(&tb.lock)
|
||||
if !ok {
|
||||
badTimer()
|
||||
}
|
||||
if raceenabled {
|
||||
raceacquire(unsafe.Pointer(t))
|
||||
}
|
||||
f(arg, seq)
|
||||
lock(&tb.lock)
|
||||
}
|
||||
if delta < 0 || faketime > 0 {
|
||||
// No timers left - put goroutine to sleep.
|
||||
tb.rescheduling = true
|
||||
goparkunlock(&tb.lock, waitReasonTimerGoroutineIdle, traceEvGoBlock, 1)
|
||||
continue
|
||||
}
|
||||
// At least one timer pending. Sleep until then.
|
||||
tb.sleeping = true
|
||||
tb.sleepUntil = now + delta
|
||||
noteclear(&tb.waitnote)
|
||||
unlock(&tb.lock)
|
||||
notetsleepg(&tb.waitnote, delta)
|
||||
}
|
||||
}
|
||||
|
||||
// cleantimers cleans up the head of the timer queue. This speeds up
|
||||
// programs that create and delete timers; leaving them in the heap
|
||||
// slows down addtimer. Reports whether no timer problems were found.
|
||||
@ -1199,55 +915,9 @@ func timejump() *p {
|
||||
return minP
|
||||
}
|
||||
|
||||
func timejumpOld() *g {
|
||||
if faketime == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := range timers {
|
||||
lock(&timers[i].lock)
|
||||
}
|
||||
gp := timejumpLocked()
|
||||
for i := range timers {
|
||||
unlock(&timers[i].lock)
|
||||
}
|
||||
|
||||
return gp
|
||||
}
|
||||
|
||||
func timejumpLocked() *g {
|
||||
// Determine a timer bucket with minimum when.
|
||||
var minT *timer
|
||||
for i := range timers {
|
||||
tb := &timers[i]
|
||||
if !tb.created || len(tb.t) == 0 {
|
||||
continue
|
||||
}
|
||||
t := tb.t[0]
|
||||
if minT == nil || t.when < minT.when {
|
||||
minT = t
|
||||
}
|
||||
}
|
||||
if minT == nil || minT.when <= faketime {
|
||||
return nil
|
||||
}
|
||||
|
||||
faketime = minT.when
|
||||
tb := minT.tb
|
||||
if !tb.rescheduling {
|
||||
return nil
|
||||
}
|
||||
tb.rescheduling = false
|
||||
return tb.gp
|
||||
}
|
||||
|
||||
// timeSleepUntil returns the time when the next timer should fire.
|
||||
// This is only called by sysmon.
|
||||
func timeSleepUntil() int64 {
|
||||
if oldTimers {
|
||||
return timeSleepUntilOld()
|
||||
}
|
||||
|
||||
next := int64(maxWhen)
|
||||
|
||||
// Prevent allp slice changes. This is like retake.
|
||||
@ -1298,27 +968,6 @@ func timeSleepUntil() int64 {
|
||||
return next
|
||||
}
|
||||
|
||||
func timeSleepUntilOld() int64 {
|
||||
next := int64(1<<63 - 1)
|
||||
|
||||
// Determine minimum sleepUntil across all the timer buckets.
|
||||
//
|
||||
// The function can not return a precise answer,
|
||||
// as another timer may pop in as soon as timers have been unlocked.
|
||||
// So lock the timers one by one instead of all at once.
|
||||
for i := range timers {
|
||||
tb := &timers[i]
|
||||
|
||||
lock(&tb.lock)
|
||||
if tb.sleeping && tb.sleepUntil < next {
|
||||
next = tb.sleepUntil
|
||||
}
|
||||
unlock(&tb.lock)
|
||||
}
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
// Heap maintenance algorithms.
|
||||
// These algorithms check for slice index errors manually.
|
||||
// Slice index error can happen if the program is using racy
|
||||
@ -1326,9 +975,6 @@ func timeSleepUntilOld() int64 {
|
||||
// it will cause the program to crash with a mysterious
|
||||
// "panic holding locks" message. Instead, we panic while not
|
||||
// holding a lock.
|
||||
// The races can occur despite the bucket locks because assignBucket
|
||||
// itself is called without locks, so racy calls can cause a timer to
|
||||
// change buckets while executing these functions.
|
||||
|
||||
func siftupTimer(t []*timer, i int) bool {
|
||||
if i >= len(t) {
|
||||
@ -1342,12 +988,10 @@ func siftupTimer(t []*timer, i int) bool {
|
||||
break
|
||||
}
|
||||
t[i] = t[p]
|
||||
t[i].i = i
|
||||
i = p
|
||||
}
|
||||
if tmp != t[i] {
|
||||
t[i] = tmp
|
||||
t[i].i = i
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -1385,12 +1029,10 @@ func siftdownTimer(t []*timer, i int) bool {
|
||||
break
|
||||
}
|
||||
t[i] = t[c]
|
||||
t[i].i = i
|
||||
i = c
|
||||
}
|
||||
if tmp != t[i] {
|
||||
t[i] = tmp
|
||||
t[i].i = i
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ const (
|
||||
traceEvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id]
|
||||
traceEvHeapAlloc = 33 // memstats.heap_live change [timestamp, heap_alloc]
|
||||
traceEvNextGC = 34 // memstats.next_gc change [timestamp, next_gc]
|
||||
traceEvTimerGoroutine = 35 // denotes timer goroutine [timer goroutine id]
|
||||
traceEvTimerGoroutine = 35 // not currently used; previously denoted timer goroutine [timer goroutine id]
|
||||
traceEvFutileWakeup = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp]
|
||||
traceEvString = 37 // string dictionary entry [ID, length, string]
|
||||
traceEvGoStartLocal = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id]
|
||||
@ -416,13 +416,6 @@ func ReadTrace() []byte {
|
||||
var data []byte
|
||||
data = append(data, traceEvFrequency|0<<traceArgCountShift)
|
||||
data = traceAppend(data, uint64(freq))
|
||||
for i := range timers {
|
||||
tb := &timers[i]
|
||||
if tb.gp != nil {
|
||||
data = append(data, traceEvTimerGoroutine|0<<traceArgCountShift)
|
||||
data = traceAppend(data, uint64(tb.gp.goid))
|
||||
}
|
||||
}
|
||||
// This will emit a bunch of full buffers, we will pick them up
|
||||
// on the next iteration.
|
||||
trace.stackTab.dump()
|
||||
|
@ -11,10 +11,7 @@ func Sleep(d Duration)
|
||||
// Interface to timers implemented in package runtime.
|
||||
// Must be in sync with ../runtime/time.go:/^type timer
|
||||
type runtimeTimer struct {
|
||||
tb uintptr
|
||||
i int
|
||||
pp uintptr
|
||||
|
||||
pp uintptr
|
||||
when int64
|
||||
period int64
|
||||
f func(interface{}, uintptr) // NOTE: must not be closure
|
||||
|
Loading…
Reference in New Issue
Block a user