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)
}
// 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.
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
}