// Inactive timers live there too temporarily, until they are removed.
//
// deltimer:
-// timerWaiting -> timerModifying -> timerModified
-// timerModified -> timerModifying -> timerModified
+// timerWaiting -> timerLocked -> timerModified
+// timerModified -> timerLocked -> timerModified
// timerRemoved -> do nothing
-// timerRunning -> wait until status changes
-// timerModifying -> wait until status changes
+// timerLocked -> wait until status changes
// modtimer:
-// timerWaiting -> timerModifying -> timerModified
-// timerModified -> timerModifying -> timerModified
-// timerRemoved -> timerModifying -> timerWaiting
-// timerRunning -> wait until status changes
-// timerModifying -> wait until status changes
+// timerWaiting -> timerLocked -> timerModified
+// timerModified -> timerLocked -> timerModified
+// timerRemoved -> timerLocked -> timerWaiting
+// timerLocked -> wait until status changes
// adjusttimers (looks in P's timer heap):
-// timerModified -> timerModifying -> timerWaiting/timerRemoved
+// timerModified -> timerLocked -> timerWaiting/timerRemoved
// runtimer (looks in P's timer heap):
// timerRemoved -> panic: uninitialized timer
// timerWaiting -> timerWaiting or
-// timerWaiting -> timerRunning -> timerRemoved or
-// timerWaiting -> timerRunning -> timerWaiting
-// timerModifying -> wait until status changes
-// timerModified -> timerModifying -> timerWaiting/timerRemoved
-// timerRunning -> panic: concurrent runtimer calls
+// timerWaiting -> timerLocked -> timerWaiting/timerRemoved
+// timerLocked -> wait until status changes
+// timerModified -> timerLocked -> timerWaiting/timerRemoved
// Values for the timer status field.
const (
// The timer is in some P's heap.
timerWaiting
- // Running the timer function.
- // A timer will only have this status briefly.
- timerRunning
-
- // The timer is being modified.
+ // The timer is locked for exclusive use.
// The timer will only have this status briefly.
- timerModifying
+ timerLocked
// The timer has been modified to a different time.
// The new when value is in the nextwhen field.
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.
const maxWhen = 1<<63 - 1
// 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 {
- for {
- switch s := t.status.Load(); s {
- case timerWaiting, timerModified:
- // Prevent preemption while the timer is in timerModifying.
- // This could lead to a self-deadlock. See #38070.
- mp := acquirem()
- if !t.status.CompareAndSwap(s, timerModifying) {
- 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()
- }
- }
+ status, mp := t.lock()
+ if status == timerWaiting || (status == timerModified && t.nextwhen != 0) {
+ // Timer pending: stop it.
+ t.pp.ptr().deletedTimers.Add(1)
+ t.nextwhen = 0
+ t.unlock(timerModified, mp)
+ return true
+ }
+
+ // Timer already run or deleted.
+ t.unlock(status, mp)
+ return false
}
// dodeltimer0 removes timer 0 from the current P's heap.
for {
switch status = t.status.Load(); status {
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.
mp = acquirem()
- if !t.status.CompareAndSwap(status, timerModifying) {
+ if !t.status.CompareAndSwap(status, timerLocked) {
releasem(mp)
break
}
pending = false // timer already run or stopped
break loop
}
- case timerRunning, timerModifying:
+ case timerLocked:
// The timer is being run or modified, by a different P.
// Wait for it to complete.
osyield()
lock(&pp.timersLock)
doaddtimer(pp, t)
unlock(&pp.timersLock)
- if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
+ if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
}
releasem(mp)
}
// Set the new status of the timer.
- if !t.status.CompareAndSwap(timerModifying, timerModified) {
+ if !t.status.CompareAndSwap(timerLocked, timerModified) {
badTimer()
}
releasem(mp)
}
switch s := t.status.Load(); s {
case timerModified:
- if !t.status.CompareAndSwap(s, timerModifying) {
+ if !t.status.CompareAndSwap(s, timerLocked) {
continue
}
if t.nextwhen == 0 {
dodeltimer0(pp)
pp.deletedTimers.Add(-1)
- if !t.status.CompareAndSwap(timerModifying, timerRemoved) {
+ if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
badTimer()
}
} else {
// Move t to the right position.
dodeltimer0(pp)
doaddtimer(pp, t)
- if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
+ if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
}
}
for {
switch s := t.status.Load(); s {
case timerWaiting:
- if !t.status.CompareAndSwap(s, timerModifying) {
+ if !t.status.CompareAndSwap(s, timerLocked) {
continue
}
t.pp = 0
doaddtimer(pp, t)
- if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
+ if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
}
break loop
case timerModified:
- if !t.status.CompareAndSwap(s, timerModifying) {
+ if !t.status.CompareAndSwap(s, timerLocked) {
continue
}
t.pp = 0
if t.nextwhen != 0 {
t.when = t.nextwhen
doaddtimer(pp, t)
- if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
+ if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
}
} else {
- if !t.status.CompareAndSwap(timerModifying, timerRemoved) {
+ if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
continue
}
}
break loop
- case timerModifying:
+ case timerLocked:
// Loop until the modification is complete.
osyield()
case timerRemoved:
// We should not see these status values in a timers heap.
badTimer()
- case timerRunning:
- // Some other P thinks it owns this timer,
- // which should not happen.
- badTimer()
default:
badTimer()
}
}
switch s := t.status.Load(); s {
case timerModified:
- if !t.status.CompareAndSwap(s, timerModifying) {
+ if !t.status.CompareAndSwap(s, timerLocked) {
// TODO(rsc): Try harder to lock.
break
}
pp.timers[n-1] = nil
pp.timers = pp.timers[:n-1]
t.pp = 0
- if !t.status.CompareAndSwap(timerModifying, timerRemoved) {
+ if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
badTimer()
}
pp.deletedTimers.Add(-1)
// Now we can change the when field.
t.when = t.nextwhen
changed = true
- if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
+ if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
}
}
- case timerRunning, timerRemoved:
+ case timerRemoved:
badTimer()
case timerWaiting:
// OK, nothing to do.
- case timerModifying:
+ case timerLocked:
// Check again after modification is complete.
osyield()
i--
return t.when
}
- if !t.status.CompareAndSwap(s, timerRunning) {
+ if !t.status.CompareAndSwap(s, timerLocked) {
continue
}
// Note that runOneTimer may temporarily unlock
return 0
case timerModified:
- if !t.status.CompareAndSwap(s, timerModifying) {
+ if !t.status.CompareAndSwap(s, timerLocked) {
continue
}
if t.nextwhen == 0 {
dodeltimer0(pp)
- if !t.status.CompareAndSwap(timerModifying, timerRemoved) {
+ if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
badTimer()
}
pp.deletedTimers.Add(-1)
t.when = t.nextwhen
dodeltimer0(pp)
doaddtimer(pp, t)
- if !t.status.CompareAndSwap(timerModifying, timerWaiting) {
+ if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
}
}
- case timerModifying:
+ case timerLocked:
// Wait for modification to complete.
osyield()
case timerRemoved:
// Should not see a new or inactive timer on the heap.
badTimer()
- case timerRunning:
- // These should only be set when timers are locked,
- // and we didn't do it.
- badTimer()
default:
badTimer()
}
t.when = maxWhen
}
siftdownTimer(pp.timers, 0)
- if !t.status.CompareAndSwap(timerRunning, timerWaiting) {
+ if !t.status.CompareAndSwap(timerLocked, timerWaiting) {
badTimer()
}
updateTimer0When(pp)
} else {
// Remove from heap.
dodeltimer0(pp)
- if !t.status.CompareAndSwap(timerRunning, timerRemoved) {
+ if !t.status.CompareAndSwap(timerLocked, timerRemoved) {
badTimer()
}
}