mirror of
https://github.com/golang/go
synced 2024-11-25 20:27:57 -07:00
runtime: use timer.lock in deltimer
The state set is now simplified enough that all the CAS loops are starting to look the same: they are just spin locks. So introduce an actual timer.lock method and use it in deltimer. [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: Ifd7f20eeede5c764ef10ecba64855c29a5ddbe39 Reviewed-on: https://go-review.googlesource.com/c/go/+/564124 Reviewed-by: Ian Lance Taylor <iant@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
db8c6c8c7a
commit
51df232b12
@ -71,27 +71,23 @@ type timer struct {
|
|||||||
// Inactive timers live there too temporarily, until they are removed.
|
// Inactive timers live there too temporarily, until they are removed.
|
||||||
//
|
//
|
||||||
// deltimer:
|
// deltimer:
|
||||||
// timerWaiting -> timerModifying -> timerModified
|
// timerWaiting -> timerLocked -> timerModified
|
||||||
// timerModified -> timerModifying -> timerModified
|
// timerModified -> timerLocked -> timerModified
|
||||||
// timerRemoved -> do nothing
|
// timerRemoved -> do nothing
|
||||||
// timerRunning -> wait until status changes
|
// timerLocked -> wait until status changes
|
||||||
// timerModifying -> wait until status changes
|
|
||||||
// modtimer:
|
// modtimer:
|
||||||
// timerWaiting -> timerModifying -> timerModified
|
// timerWaiting -> timerLocked -> timerModified
|
||||||
// timerModified -> timerModifying -> timerModified
|
// timerModified -> timerLocked -> timerModified
|
||||||
// timerRemoved -> timerModifying -> timerWaiting
|
// timerRemoved -> timerLocked -> timerWaiting
|
||||||
// timerRunning -> wait until status changes
|
// timerLocked -> wait until status changes
|
||||||
// timerModifying -> wait until status changes
|
|
||||||
// adjusttimers (looks in P's timer heap):
|
// adjusttimers (looks in P's timer heap):
|
||||||
// timerModified -> timerModifying -> timerWaiting/timerRemoved
|
// timerModified -> timerLocked -> timerWaiting/timerRemoved
|
||||||
// runtimer (looks in P's timer heap):
|
// runtimer (looks in P's timer heap):
|
||||||
// timerRemoved -> panic: uninitialized timer
|
// timerRemoved -> panic: uninitialized timer
|
||||||
// timerWaiting -> timerWaiting or
|
// timerWaiting -> timerWaiting or
|
||||||
// timerWaiting -> timerRunning -> timerRemoved or
|
// timerWaiting -> timerLocked -> timerWaiting/timerRemoved
|
||||||
// timerWaiting -> timerRunning -> timerWaiting
|
// timerLocked -> wait until status changes
|
||||||
// timerModifying -> wait until status changes
|
// timerModified -> timerLocked -> timerWaiting/timerRemoved
|
||||||
// timerModified -> timerModifying -> timerWaiting/timerRemoved
|
|
||||||
// timerRunning -> panic: concurrent runtimer calls
|
|
||||||
|
|
||||||
// Values for the timer status field.
|
// Values for the timer status field.
|
||||||
const (
|
const (
|
||||||
@ -103,13 +99,9 @@ const (
|
|||||||
// The timer is in some P's heap.
|
// The timer is in some P's heap.
|
||||||
timerWaiting
|
timerWaiting
|
||||||
|
|
||||||
// Running the timer function.
|
// The timer is locked for exclusive use.
|
||||||
// A timer will only have this status briefly.
|
|
||||||
timerRunning
|
|
||||||
|
|
||||||
// The timer is being modified.
|
|
||||||
// The timer will only have this status briefly.
|
// The timer will only have this status briefly.
|
||||||
timerModifying
|
timerLocked
|
||||||
|
|
||||||
// The timer has been modified to a different time.
|
// The timer has been modified to a different time.
|
||||||
// The new when value is in the nextwhen field.
|
// The new when value is in the nextwhen field.
|
||||||
@ -118,6 +110,38 @@ const (
|
|||||||
timerModified
|
timerModified
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// lock locks the timer, allowing reading or writing any of the timer fields.
|
||||||
|
// It returns the current m and the status prior to the lock.
|
||||||
|
// The caller must call unlock with the same m and an updated status.
|
||||||
|
func (t *timer) lock() (status uint32, mp *m) {
|
||||||
|
for {
|
||||||
|
status := t.status.Load()
|
||||||
|
if status == timerLocked {
|
||||||
|
osyield()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Prevent preemption while the timer is locked.
|
||||||
|
// This could lead to a self-deadlock. See #38070.
|
||||||
|
mp := acquirem()
|
||||||
|
if t.status.CompareAndSwap(status, timerLocked) {
|
||||||
|
return status, mp
|
||||||
|
}
|
||||||
|
releasem(mp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlock unlocks the timer.
|
||||||
|
func (t *timer) unlock(status uint32, mp *m) {
|
||||||
|
if t.status.Load() != timerLocked {
|
||||||
|
badTimer()
|
||||||
|
}
|
||||||
|
if status == timerLocked {
|
||||||
|
badTimer()
|
||||||
|
}
|
||||||
|
t.status.Store(status)
|
||||||
|
releasem(mp)
|
||||||
|
}
|
||||||
|
|
||||||
// maxWhen is the maximum value for timer's when field.
|
// maxWhen is the maximum value for timer's when field.
|
||||||
const maxWhen = 1<<63 - 1
|
const maxWhen = 1<<63 - 1
|
||||||
|
|
||||||
@ -237,46 +261,18 @@ func doaddtimer(pp *p, t *timer) {
|
|||||||
// It will be removed in due course by the P whose heap it is on.
|
// 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.
|
// Reports whether the timer was removed before it was run.
|
||||||
func deltimer(t *timer) bool {
|
func deltimer(t *timer) bool {
|
||||||
for {
|
status, mp := t.lock()
|
||||||
switch s := t.status.Load(); s {
|
if status == timerWaiting || (status == timerModified && t.nextwhen != 0) {
|
||||||
case timerWaiting, timerModified:
|
// Timer pending: stop it.
|
||||||
// Prevent preemption while the timer is in timerModifying.
|
t.pp.ptr().deletedTimers.Add(1)
|
||||||
// This could lead to a self-deadlock. See #38070.
|
t.nextwhen = 0
|
||||||
mp := acquirem()
|
t.unlock(timerModified, mp)
|
||||||
if !t.status.CompareAndSwap(s, timerModifying) {
|
return true
|
||||||
releasem(mp)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if s == timerModified && t.nextwhen == 0 {
|
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerModified) {
|
|
||||||
badTimer()
|
|
||||||
}
|
|
||||||
releasem(mp)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Must fetch t.pp before changing status,
|
|
||||||
// as cleantimers in another goroutine
|
|
||||||
// can clear t.pp of a deleted timer.
|
|
||||||
t.pp.ptr().deletedTimers.Add(1)
|
|
||||||
t.nextwhen = 0
|
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerModified) {
|
|
||||||
badTimer()
|
|
||||||
}
|
|
||||||
releasem(mp)
|
|
||||||
// Timer was not yet run.
|
|
||||||
return true
|
|
||||||
|
|
||||||
case timerRemoved:
|
|
||||||
// Timer was already run.
|
|
||||||
return false
|
|
||||||
case timerRunning, timerModifying:
|
|
||||||
// The timer is being run or modified, by a different P.
|
|
||||||
// Wait for it to complete.
|
|
||||||
osyield()
|
|
||||||
default:
|
|
||||||
badTimer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timer already run or deleted.
|
||||||
|
t.unlock(status, mp)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// dodeltimer0 removes timer 0 from the current P's heap.
|
// dodeltimer0 removes timer 0 from the current P's heap.
|
||||||
@ -325,10 +321,10 @@ loop:
|
|||||||
for {
|
for {
|
||||||
switch status = t.status.Load(); status {
|
switch status = t.status.Load(); status {
|
||||||
case timerWaiting, timerModified, timerRemoved:
|
case timerWaiting, timerModified, timerRemoved:
|
||||||
// Prevent preemption while the timer is in timerModifying.
|
// Prevent preemption while the timer is in timerLocked.
|
||||||
// This could lead to a self-deadlock. See #38070.
|
// This could lead to a self-deadlock. See #38070.
|
||||||
mp = acquirem()
|
mp = acquirem()
|
||||||
if !t.status.CompareAndSwap(status, timerModifying) {
|
if !t.status.CompareAndSwap(status, timerLocked) {
|
||||||
releasem(mp)
|
releasem(mp)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -346,7 +342,7 @@ loop:
|
|||||||
pending = false // timer already run or stopped
|
pending = false // timer already run or stopped
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
case timerRunning, timerModifying:
|
case timerLocked:
|
||||||
// The timer is being run or modified, by a different P.
|
// The timer is being run or modified, by a different P.
|
||||||
// Wait for it to complete.
|
// Wait for it to complete.
|
||||||
osyield()
|
osyield()
|
||||||
@ -366,7 +362,7 @@ loop:
|
|||||||
lock(&pp.timersLock)
|
lock(&pp.timersLock)
|
||||||
doaddtimer(pp, t)
|
doaddtimer(pp, t)
|
||||||
unlock(&pp.timersLock)
|
unlock(&pp.timersLock)
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
|
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
releasem(mp)
|
releasem(mp)
|
||||||
@ -384,7 +380,7 @@ loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the new status of the timer.
|
// Set the new status of the timer.
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerModified) {
|
if !t.status.CompareAndSwap(timerLocked, timerModified) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
releasem(mp)
|
releasem(mp)
|
||||||
@ -430,13 +426,13 @@ func cleantimers(pp *p) {
|
|||||||
}
|
}
|
||||||
switch s := t.status.Load(); s {
|
switch s := t.status.Load(); s {
|
||||||
case timerModified:
|
case timerModified:
|
||||||
if !t.status.CompareAndSwap(s, timerModifying) {
|
if !t.status.CompareAndSwap(s, timerLocked) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if t.nextwhen == 0 {
|
if t.nextwhen == 0 {
|
||||||
dodeltimer0(pp)
|
dodeltimer0(pp)
|
||||||
pp.deletedTimers.Add(-1)
|
pp.deletedTimers.Add(-1)
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerRemoved) {
|
if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -445,7 +441,7 @@ func cleantimers(pp *p) {
|
|||||||
// Move t to the right position.
|
// Move t to the right position.
|
||||||
dodeltimer0(pp)
|
dodeltimer0(pp)
|
||||||
doaddtimer(pp, t)
|
doaddtimer(pp, t)
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
|
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -487,42 +483,38 @@ func moveTimers(pp *p, timers []*timer) {
|
|||||||
for {
|
for {
|
||||||
switch s := t.status.Load(); s {
|
switch s := t.status.Load(); s {
|
||||||
case timerWaiting:
|
case timerWaiting:
|
||||||
if !t.status.CompareAndSwap(s, timerModifying) {
|
if !t.status.CompareAndSwap(s, timerLocked) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.pp = 0
|
t.pp = 0
|
||||||
doaddtimer(pp, t)
|
doaddtimer(pp, t)
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
|
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
break loop
|
break loop
|
||||||
case timerModified:
|
case timerModified:
|
||||||
if !t.status.CompareAndSwap(s, timerModifying) {
|
if !t.status.CompareAndSwap(s, timerLocked) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.pp = 0
|
t.pp = 0
|
||||||
if t.nextwhen != 0 {
|
if t.nextwhen != 0 {
|
||||||
t.when = t.nextwhen
|
t.when = t.nextwhen
|
||||||
doaddtimer(pp, t)
|
doaddtimer(pp, t)
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
|
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerRemoved) {
|
if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break loop
|
break loop
|
||||||
case timerModifying:
|
case timerLocked:
|
||||||
// Loop until the modification is complete.
|
// Loop until the modification is complete.
|
||||||
osyield()
|
osyield()
|
||||||
case timerRemoved:
|
case timerRemoved:
|
||||||
// We should not see these status values in a timers heap.
|
// We should not see these status values in a timers heap.
|
||||||
badTimer()
|
badTimer()
|
||||||
case timerRunning:
|
|
||||||
// Some other P thinks it owns this timer,
|
|
||||||
// which should not happen.
|
|
||||||
badTimer()
|
|
||||||
default:
|
default:
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
@ -562,7 +554,7 @@ func adjusttimers(pp *p, now int64, force bool) {
|
|||||||
}
|
}
|
||||||
switch s := t.status.Load(); s {
|
switch s := t.status.Load(); s {
|
||||||
case timerModified:
|
case timerModified:
|
||||||
if !t.status.CompareAndSwap(s, timerModifying) {
|
if !t.status.CompareAndSwap(s, timerLocked) {
|
||||||
// TODO(rsc): Try harder to lock.
|
// TODO(rsc): Try harder to lock.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -572,7 +564,7 @@ func adjusttimers(pp *p, now int64, force bool) {
|
|||||||
pp.timers[n-1] = nil
|
pp.timers[n-1] = nil
|
||||||
pp.timers = pp.timers[:n-1]
|
pp.timers = pp.timers[:n-1]
|
||||||
t.pp = 0
|
t.pp = 0
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerRemoved) {
|
if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
pp.deletedTimers.Add(-1)
|
pp.deletedTimers.Add(-1)
|
||||||
@ -582,15 +574,15 @@ func adjusttimers(pp *p, now int64, force bool) {
|
|||||||
// Now we can change the when field.
|
// Now we can change the when field.
|
||||||
t.when = t.nextwhen
|
t.when = t.nextwhen
|
||||||
changed = true
|
changed = true
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
|
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case timerRunning, timerRemoved:
|
case timerRemoved:
|
||||||
badTimer()
|
badTimer()
|
||||||
case timerWaiting:
|
case timerWaiting:
|
||||||
// OK, nothing to do.
|
// OK, nothing to do.
|
||||||
case timerModifying:
|
case timerLocked:
|
||||||
// Check again after modification is complete.
|
// Check again after modification is complete.
|
||||||
osyield()
|
osyield()
|
||||||
i--
|
i--
|
||||||
@ -708,7 +700,7 @@ func runtimer(pp *p, now int64) int64 {
|
|||||||
return t.when
|
return t.when
|
||||||
}
|
}
|
||||||
|
|
||||||
if !t.status.CompareAndSwap(s, timerRunning) {
|
if !t.status.CompareAndSwap(s, timerLocked) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Note that runOneTimer may temporarily unlock
|
// Note that runOneTimer may temporarily unlock
|
||||||
@ -717,12 +709,12 @@ func runtimer(pp *p, now int64) int64 {
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
case timerModified:
|
case timerModified:
|
||||||
if !t.status.CompareAndSwap(s, timerModifying) {
|
if !t.status.CompareAndSwap(s, timerLocked) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if t.nextwhen == 0 {
|
if t.nextwhen == 0 {
|
||||||
dodeltimer0(pp)
|
dodeltimer0(pp)
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerRemoved) {
|
if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
pp.deletedTimers.Add(-1)
|
pp.deletedTimers.Add(-1)
|
||||||
@ -733,22 +725,18 @@ func runtimer(pp *p, now int64) int64 {
|
|||||||
t.when = t.nextwhen
|
t.when = t.nextwhen
|
||||||
dodeltimer0(pp)
|
dodeltimer0(pp)
|
||||||
doaddtimer(pp, t)
|
doaddtimer(pp, t)
|
||||||
if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
|
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case timerModifying:
|
case timerLocked:
|
||||||
// Wait for modification to complete.
|
// Wait for modification to complete.
|
||||||
osyield()
|
osyield()
|
||||||
|
|
||||||
case timerRemoved:
|
case timerRemoved:
|
||||||
// Should not see a new or inactive timer on the heap.
|
// Should not see a new or inactive timer on the heap.
|
||||||
badTimer()
|
badTimer()
|
||||||
case timerRunning:
|
|
||||||
// These should only be set when timers are locked,
|
|
||||||
// and we didn't do it.
|
|
||||||
badTimer()
|
|
||||||
default:
|
default:
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
@ -781,14 +769,14 @@ func runOneTimer(pp *p, t *timer, now int64) {
|
|||||||
t.when = maxWhen
|
t.when = maxWhen
|
||||||
}
|
}
|
||||||
siftdownTimer(pp.timers, 0)
|
siftdownTimer(pp.timers, 0)
|
||||||
if !t.status.CompareAndSwap(timerRunning, timerWaiting) {
|
if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
updateTimer0When(pp)
|
updateTimer0When(pp)
|
||||||
} else {
|
} else {
|
||||||
// Remove from heap.
|
// Remove from heap.
|
||||||
dodeltimer0(pp)
|
dodeltimer0(pp)
|
||||||
if !t.status.CompareAndSwap(timerRunning, timerRemoved) {
|
if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
|
||||||
badTimer()
|
badTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user