]> Cypherpunks repositories - gostls13.git/commitdiff
runtime, time: remove old timer code
authorIan Lance Taylor <iant@golang.org>
Fri, 12 Apr 2019 03:23:35 +0000 (20:23 -0700)
committerIan Lance Taylor <iant@golang.org>
Tue, 19 Nov 2019 15:30:58 +0000 (15:30 +0000)
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>
src/runtime/proc.go
src/runtime/time.go
src/runtime/trace.go
src/time/sleep.go

index c648195271537a6605ffe2de1ad73a6e03998efa..d264e1d1208d4d4db4984bc983fa38ba95cee231 100644 (file)
@@ -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")
-                       }
-                       mp := mget()
-                       if mp == nil {
-                               // There should always be a free M since
-                               // nothing is running.
-                               throw("checkdead: no m 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.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.
index ed044b3b4cffaefdda8810efff3d43a152a32332..a7d14cf8777931b2a9939ba778cb458ef9184564 100644 (file)
@@ -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
 }
index 65d9a38052f9d5bc3ce38d78bae242c279d537c4..9aa9facabee43728565581fa146240bdbb11ebc5 100644 (file)
@@ -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()
index adce860b3068c58c3599c4b6cd0193d8ee58ddd5..4e61d0a6c1383087ece05f45baa220d063a8c4eb 100644 (file)
@@ -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