runtime: use timer.lock in runtimer

Continue using timer.lock to simplify timer operations.

[This is one CL in a refactoring stack making very small changes
in each step, so that any subtle bugs that we miss can be more
easily pinpointed to a small change.]

Change-Id: I504335a010d6eb4d7d627145b64a896582158406
Reviewed-on: https://go-review.googlesource.com/c/go/+/564129
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Russ Cox 2024-02-14 11:57:03 -05:00
parent 58911599e8
commit d1e8dc25ff

View File

@ -657,67 +657,57 @@ func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) {
//
//go:systemstack
func runtimer(pp *p, now int64) int64 {
for {
Redo:
if len(pp.timers) == 0 {
return -1
}
t := pp.timers[0]
if t.pp.ptr() != pp {
throw("runtimer: bad p")
}
switch s := t.status.Load(); s {
case timerWaiting:
if t.when > now {
// Not ready to run.
if t.status.Load() == timerWaiting && t.when > now {
// Fast path: not ready to run.
// The access of t.when is protected by the caller holding
// pp.timersLock, even though t itself is unlocked.
return t.when
}
if !t.status.CompareAndSwap(s, timerLocked) {
continue
}
// Note that runOneTimer may temporarily unlock
// pp.timersLock.
runOneTimer(pp, t, now)
return 0
case timerModified:
if !t.status.CompareAndSwap(s, timerLocked) {
continue
}
if t.nextwhen == 0 {
status, mp := t.lock()
if status == timerModified {
dodeltimer0(pp)
if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
badTimer()
}
if t.nextwhen == 0 {
status = timerRemoved
pp.deletedTimers.Add(-1)
if len(pp.timers) == 0 {
return -1
}
} else {
t.when = t.nextwhen
dodeltimer0(pp)
doaddtimer(pp, t)
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
status = timerWaiting
}
t.unlock(status, mp)
goto Redo
}
case timerLocked:
// Wait for modification to complete.
osyield()
if status != timerWaiting {
badTimer()
}
case timerRemoved:
// Should not see a new or inactive timer on the heap.
badTimer()
default:
badTimer()
}
if t.when > now {
// Not ready to run.
t.unlock(status, mp)
return t.when
}
unlockAndRunTimer(pp, t, now, status, mp)
return 0
}
// runOneTimer runs a single timer.
// unlockAndRunTimer unlocks and runs a single timer.
// The caller must have locked the timers for pp.
// This will temporarily unlock the timers while running the timer function.
//
//go:systemstack
func runOneTimer(pp *p, t *timer, now int64) {
func unlockAndRunTimer(pp *p, t *timer, now int64, status uint32, mp *m) {
if raceenabled {
ppcur := getg().m.p.ptr()
if ppcur.timerRaceCtx == 0 {
@ -738,17 +728,14 @@ func runOneTimer(pp *p, t *timer, now int64) {
t.when = maxWhen
}
siftdownTimer(pp.timers, 0)
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
}
status = timerWaiting
updateTimer0When(pp)
} else {
// Remove from heap.
dodeltimer0(pp)
if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
badTimer()
}
status = timerRemoved
}
t.unlock(status, mp)
if raceenabled {
// Temporarily use the current P's racectx for g0.