Skip to content

Commit 48eb79e

Browse files
runtime: add new modtimer function
This adds a new field to P, adjustTimers, that tells the P that one of its existing timers was modified to be earlier, and that it therefore needs to resort them. Updates #27707 Change-Id: I4c5f5b51ed116f1d898d3f87cdddfa1b552337f8 Reviewed-on: https://go-review.googlesource.com/c/go/+/171832 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Michael Knyszek <mknyszek@google.com>
1 parent a813d3c commit 48eb79e

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

src/runtime/proc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4122,6 +4122,7 @@ func (pp *p) destroy() {
41224122
// The world is stopped so we don't need to hold timersLock.
41234123
moveTimers(plocal, pp.timers)
41244124
pp.timers = nil
4125+
pp.adjustTimers = 0
41254126
}
41264127
// If there's a background worker, make it runnable and put
41274128
// it on the global queue so it can clean itself up.

src/runtime/runtime2.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,12 @@ type p struct {
607607
// Must hold timersLock to access.
608608
timers []*timer
609609

610+
// Number of timerModifiedEarlier timers on P's heap.
611+
// This should only be modified while holding timersLock,
612+
// or while the timer status is in a transient state
613+
// such as timerModifying.
614+
adjustTimers uint32
615+
610616
pad cpu.CacheLinePad
611617
}
612618

src/runtime/time.go

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ type timersBucket struct {
131131
// timerRunning -> wait until status changes
132132
// timerMoving -> wait until status changes
133133
// timerModifying -> panic: concurrent deltimer/modtimer calls
134+
// modtimer:
135+
// timerWaiting -> timerModifying -> timerModifiedXX
136+
// timerModifiedXX -> timerModifying -> timerModifiedYY
137+
// timerNoStatus -> timerWaiting
138+
// timerRemoved -> timerWaiting
139+
// timerRunning -> wait until status changes
140+
// timerMoving -> wait until status changes
141+
// timerRemoving -> wait until status changes
142+
// timerDeleted -> panic: concurrent modtimer/deltimer calls
143+
// timerModifying -> panic: concurrent modtimer calls
134144

135145
// Values for the timer status field.
136146
const (
@@ -270,6 +280,11 @@ func addtimer(t *timer) {
270280
}
271281
t.status = timerWaiting
272282

283+
addInitializedTimer(t)
284+
}
285+
286+
// addInitializedTimer adds an initialized timer to the current P.
287+
func addInitializedTimer(t *timer) {
273288
when := t.when
274289

275290
pp := getg().m.p.ptr()
@@ -363,7 +378,9 @@ func deltimer(t *timer) bool {
363378
return true
364379
}
365380
case timerModifiedEarlier:
381+
tpp := t.pp.ptr()
366382
if atomic.Cas(&t.status, s, timerModifying) {
383+
atomic.Xadd(&tpp.adjustTimers, -1)
367384
if !atomic.Cas(&t.status, timerModifying, timerDeleted) {
368385
badTimer()
369386
}
@@ -438,12 +455,94 @@ func (tb *timersBucket) deltimerLocked(t *timer) (removed, ok bool) {
438455
return true, ok
439456
}
440457

458+
// modtimer modifies an existing timer.
459+
// This is called by the netpoll code.
441460
func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) {
442461
if oldTimers {
443462
modtimerOld(t, when, period, f, arg, seq)
444463
return
445464
}
446-
throw("new modtimer not yet implemented")
465+
466+
if when < 0 {
467+
when = maxWhen
468+
}
469+
470+
status := uint32(timerNoStatus)
471+
wasRemoved := false
472+
loop:
473+
for {
474+
switch status = atomic.Load(&t.status); status {
475+
case timerWaiting, timerModifiedEarlier, timerModifiedLater:
476+
if atomic.Cas(&t.status, status, timerModifying) {
477+
break loop
478+
}
479+
case timerNoStatus, timerRemoved:
480+
// Timer was already run and t is no longer in a heap.
481+
// Act like addtimer.
482+
wasRemoved = true
483+
atomic.Store(&t.status, timerWaiting)
484+
break loop
485+
case timerRunning, timerRemoving, timerMoving:
486+
// The timer is being run or moved, by a different P.
487+
// Wait for it to complete.
488+
osyield()
489+
case timerDeleted:
490+
// Simultaneous calls to modtimer and deltimer.
491+
badTimer()
492+
case timerModifying:
493+
// Multiple simultaneous calls to modtimer.
494+
badTimer()
495+
default:
496+
badTimer()
497+
}
498+
}
499+
500+
t.period = period
501+
t.f = f
502+
t.arg = arg
503+
t.seq = seq
504+
505+
if wasRemoved {
506+
t.when = when
507+
addInitializedTimer(t)
508+
} else {
509+
// The timer is in some other P's heap, so we can't change
510+
// the when field. If we did, the other P's heap would
511+
// be out of order. So we put the new when value in the
512+
// nextwhen field, and let the other P set the when field
513+
// when it is prepared to resort the heap.
514+
t.nextwhen = when
515+
516+
newStatus := uint32(timerModifiedLater)
517+
if when < t.when {
518+
newStatus = timerModifiedEarlier
519+
}
520+
521+
// Update the adjustTimers field. Subtract one if we
522+
// are removing a timerModifiedEarlier, add one if we
523+
// are adding a timerModifiedEarlier.
524+
tpp := t.pp.ptr()
525+
adjust := int32(0)
526+
if status == timerModifiedEarlier {
527+
adjust--
528+
}
529+
if newStatus == timerModifiedEarlier {
530+
adjust++
531+
}
532+
if adjust != 0 {
533+
atomic.Xadd(&tpp.adjustTimers, adjust)
534+
}
535+
536+
// Set the new status of the timer.
537+
if !atomic.Cas(&t.status, timerModifying, newStatus) {
538+
badTimer()
539+
}
540+
541+
// If the new status is earlier, wake up the poller.
542+
if newStatus == timerModifiedEarlier {
543+
wakeNetPoller(when)
544+
}
545+
}
447546
}
448547

449548
func modtimerOld(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) {

0 commit comments

Comments
 (0)