aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/time.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/time.go')
-rw-r--r--src/runtime/time.go99
1 files changed, 55 insertions, 44 deletions
diff --git a/src/runtime/time.go b/src/runtime/time.go
index f895bf8443..99290f66d0 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -491,6 +491,8 @@ loop:
newStatus = timerModifiedEarlier
}
+ tpp := t.pp.ptr()
+
// Update the adjustTimers field. Subtract one if we
// are removing a timerModifiedEarlier, add one if we
// are adding a timerModifiedEarlier.
@@ -500,9 +502,10 @@ loop:
}
if newStatus == timerModifiedEarlier {
adjust++
+ updateTimerModifiedEarliest(tpp, when)
}
if adjust != 0 {
- atomic.Xadd(&t.pp.ptr().adjustTimers, adjust)
+ atomic.Xadd(&tpp.adjustTimers, adjust)
}
// Set the new status of the timer.
@@ -637,16 +640,36 @@ func moveTimers(pp *p, timers []*timer) {
// the correct place in the heap. While looking for those timers,
// it also moves timers that have been modified to run later,
// and removes deleted timers. The caller must have locked the timers for pp.
-func adjusttimers(pp *p) {
- if len(pp.timers) == 0 {
- return
- }
+func adjusttimers(pp *p, now int64) {
if atomic.Load(&pp.adjustTimers) == 0 {
if verifyTimers {
verifyTimerHeap(pp)
}
+ // There are no timers to adjust, so it is safe to clear
+ // timerModifiedEarliest. Do so in case it is stale.
+ // Everything will work if we don't do this,
+ // but clearing here may save future calls to adjusttimers.
+ atomic.Store64(&pp.timerModifiedEarliest, 0)
return
}
+
+ // If we haven't yet reached the time of the first timerModifiedEarlier
+ // timer, don't do anything. This speeds up programs that adjust
+ // a lot of timers back and forth if the timers rarely expire.
+ // We'll postpone looking through all the adjusted timers until
+ // one would actually expire.
+ if first := atomic.Load64(&pp.timerModifiedEarliest); first != 0 {
+ if int64(first) > now {
+ if verifyTimers {
+ verifyTimerHeap(pp)
+ }
+ return
+ }
+
+ // We are going to clear all timerModifiedEarlier timers.
+ atomic.Store64(&pp.timerModifiedEarliest, 0)
+ }
+
var moved []*timer
loop:
for i := 0; i < len(pp.timers); i++ {
@@ -868,6 +891,10 @@ func runOneTimer(pp *p, t *timer, now int64) {
//
// The caller must have locked the timers for pp.
func clearDeletedTimers(pp *p) {
+ // We are going to clear all timerModifiedEarlier timers.
+ // Do this now in case new ones show up while we are looping.
+ atomic.Store64(&pp.timerModifiedEarliest, 0)
+
cdel := int32(0)
cearlier := int32(0)
to := 0
@@ -977,6 +1004,21 @@ func updateTimer0When(pp *p) {
}
}
+// updateTimerModifiedEarliest updates the recorded nextwhen field of the
+// earlier timerModifiedEarier value.
+// The timers for pp will not be locked.
+func updateTimerModifiedEarliest(pp *p, nextwhen int64) {
+ for {
+ old := atomic.Load64(&pp.timerModifiedEarliest)
+ if old != 0 && int64(old) < nextwhen {
+ return
+ }
+ if atomic.Cas64(&pp.timerModifiedEarliest, old, uint64(nextwhen)) {
+ return
+ }
+ }
+}
+
// timeSleepUntil returns the time when the next timer should fire,
// and the P that holds the timer heap that that timer is on.
// This is only called by sysmon and checkdead.
@@ -993,48 +1035,17 @@ func timeSleepUntil() (int64, *p) {
continue
}
- c := atomic.Load(&pp.adjustTimers)
- if c == 0 {
- w := int64(atomic.Load64(&pp.timer0When))
- if w != 0 && w < next {
- next = w
- pret = pp
- }
- continue
+ w := int64(atomic.Load64(&pp.timer0When))
+ if w != 0 && w < next {
+ next = w
+ pret = pp
}
- lock(&pp.timersLock)
- for _, t := range pp.timers {
- switch s := atomic.Load(&t.status); s {
- case timerWaiting:
- if t.when < next {
- next = t.when
- }
- case timerModifiedEarlier, timerModifiedLater:
- if t.nextwhen < next {
- next = t.nextwhen
- }
- if s == timerModifiedEarlier {
- c--
- }
- }
- // 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.
- //
- // We don't worry about cases like timerModifying.
- // New timers can show up at any time,
- // so this function is necessarily imprecise.
- // Do a signed check here since we aren't
- // synchronizing the read of pp.adjustTimers
- // with the check of a timer status.
- if int32(c) <= 0 {
- break
- }
+ w = int64(atomic.Load64(&pp.timerModifiedEarliest))
+ if w != 0 && w < next {
+ next = w
+ pret = pp
}
- unlock(&pp.timersLock)
}
unlock(&allpLock)