diff --git a/src/runtime/proc.go b/src/runtime/proc.go index edff454491..aa0a1fa2be 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -2451,9 +2451,19 @@ stop: if _g_.m.spinning { throw("findrunnable: netpoll with spinning") } + if faketime != 0 { + // When using fake time, just poll. + delta = 0 + } list := netpoll(delta) // block until new work is available atomic.Store64(&sched.pollUntil, 0) atomic.Store64(&sched.lastpoll, uint64(nanotime())) + if faketime != 0 && list.empty() { + // Using fake time and nothing is ready; stop M. + // When all M's stop, checkdead will call timejump. + stopm() + goto top + } lock(&sched.lock) _p_ = pidleget() unlock(&sched.lock) @@ -4422,23 +4432,44 @@ func checkdead() { } // Maybe jump time forward for playground. - gp := timejump() - if gp != nil { - casgstatus(gp, _Gwaiting, _Grunnable) - globrunqput(gp) - _p_ := pidleget() - if _p_ == nil { - throw("checkdead: no p for timer") + 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") + } + 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") + } 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.nextp.set(_p_) - notewakeup(&mp.park) - return } // There are no goroutines running, so we can look at the P's. diff --git a/src/runtime/time.go b/src/runtime/time.go index 3eba66bf07..c0f2d0287d 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -1125,7 +1125,66 @@ func runOneTimer(pp *p, t *timer, now int64) { f(arg, seq) } -func timejump() *g { +func timejump() *p { + if faketime == 0 { + return nil + } + + // Nothing is running, so we can look at all the P's. + // Determine a timer bucket with minimum when. + var ( + minT *timer + minWhen int64 + minP *p + ) + for _, pp := range allp { + if pp.status != _Pidle && pp.status != _Pdead { + throw("non-idle P in timejump") + } + if len(pp.timers) == 0 { + continue + } + c := pp.adjustTimers + for _, t := range pp.timers { + switch s := atomic.Load(&t.status); s { + case timerWaiting: + if minT == nil || t.when < minWhen { + minT = t + minWhen = t.when + minP = pp + } + case timerModifiedEarlier, timerModifiedLater: + if minT == nil || t.nextwhen < minWhen { + minT = t + minWhen = t.nextwhen + minP = pp + } + if s == timerModifiedEarlier { + c-- + } + case timerRunning, timerModifying, timerMoving: + badTimer() + } + // The timers are sorted, so we only have to check + // the first timer for each P, unless there are + // some timerModifiedEarlier timers. The number + // of timerModifiedEarlier timers is in the adjustTimers + // field, used to initialize c, above. + if c == 0 { + break + } + } + } + + if minT == nil || minWhen <= faketime { + return nil + } + + faketime = minWhen + return minP +} + +func timejumpOld() *g { if faketime == 0 { return nil }