Skip to content

Commit

Permalink
runtime: introduce timers.lock, timers.unlock methods
Browse files Browse the repository at this point in the history
No semantic changes here.
Cleaning up for next change.

Change-Id: I9706009739677ff9eb893bcc007d805f7877511e
Reviewed-on: https://go-review.googlesource.com/c/go/+/568335
Reviewed-by: Austin Clements <[email protected]>
Auto-Submit: Russ Cox <[email protected]>
Reviewed-by: Michael Pratt <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
  • Loading branch information
rsc authored and gopherbot committed Mar 8, 2024
1 parent c220fba commit c961ab3
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/runtime/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5435,7 +5435,7 @@ func (pp *p) init(id int32) {
pp.raceprocctx = raceproccreate()
}
}
lockInit(&pp.timers.lock, lockRankTimers)
lockInit(&pp.timers.mu, lockRankTimers)

// This P may get timers when it starts running. Set the mask here
// since the P may not go through pidleget (notably P 0 on startup).
Expand Down
59 changes: 33 additions & 26 deletions src/runtime/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ type timer struct {

// A timers is a per-P set of timers.
type timers struct {
// lock protects timers; timers are per-P, but the scheduler can
// mu protects timers; timers are per-P, but the scheduler can
// access the timers of another P, so we have to lock.
lock mutex
mu mutex

// heap is the set of timers, ordered by t.when.
// Must hold lock to access.
Expand Down Expand Up @@ -84,6 +84,14 @@ type timers struct {
minNextWhen atomic.Int64
}

func (ts *timers) lock() {
lock(&ts.mu)
}

func (ts *timers) unlock() {
unlock(&ts.mu)
}

// Timer state field.
// Note that state 0 must be "unlocked, not in heap" and usable,
// at least for time.Timer.Stop. See go.dev/issue/21874.
Expand Down Expand Up @@ -159,7 +167,7 @@ func (t *timer) unlock(state uint32, mp *m) {
// temporarily not maintaining its invariant, such as during timers.adjust).
func (t *timer) updateHeap(state uint32, ts *timers) (newState uint32, updated bool) {
if ts != nil {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
}
if state&timerZombie != 0 {
// Take timer out of heap, applying final t.when update first.
Expand Down Expand Up @@ -294,7 +302,7 @@ func goroutineReady(arg any, seq uintptr) {
// Callers that are not sure can call t.maybeAdd instead,
// but note that maybeAdd has different locking requirements.
func (ts *timers) addHeap(t *timer) {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
// Timers rely on the network poller, so make sure the poller
// has started.
if netpollInited.Load() == 0 {
Expand Down Expand Up @@ -343,7 +351,7 @@ func (t *timer) stop() bool {
// deleteMin removes timer 0 from ts.
// ts must be locked.
func (ts *timers) deleteMin() {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
t := ts.heap[0]
if t.ts != ts {
throw("wrong timers")
Expand Down Expand Up @@ -453,7 +461,7 @@ func (t *timer) needsAdd(state uint32) bool {
// t is not in a heap on entry to t.maybeAdd.
func (t *timer) maybeAdd() {
ts := &getg().m.p.ptr().timers
lock(&ts.lock)
ts.lock()
state, mp := t.lock()
when := int64(0)
if t.needsAdd(state) {
Expand All @@ -462,7 +470,7 @@ func (t *timer) maybeAdd() {
when = t.when
}
t.unlock(state, mp)
unlock(&ts.lock)
ts.unlock()
if when > 0 {
wakeNetPoller(when)
}
Expand All @@ -480,7 +488,7 @@ func (t *timer) reset(when int64) bool {
// slows down heap operations.
// The caller must have locked ts.
func (ts *timers) cleanHead() {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
gp := getg()
for {
if len(ts.heap) == 0 {
Expand Down Expand Up @@ -528,15 +536,15 @@ func (ts *timers) take(src *timers) {
// protect against sysmon calling timeSleepUntil.
// This is the only case where we hold more than one ts.lock,
// so there are no deadlock concerns.
lock(&src.lock)
lock(&ts.lock)
src.lock()
ts.lock()
ts.move(src.heap)
src.heap = nil
src.len.Store(0)
src.zombies.Store(0)
src.minWhen.Store(0)
unlock(&ts.lock)
unlock(&src.lock)
ts.unlock()
src.unlock()
}
}

Expand All @@ -545,7 +553,7 @@ func (ts *timers) take(src *timers) {
// This is currently called when the world is stopped, but the caller
// is expected to have locked ts.
func (ts *timers) move(timers []*timer) {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
for _, t := range timers {
state, mp := t.lock()
t.ts = nil
Expand All @@ -563,7 +571,7 @@ func (ts *timers) move(timers []*timer) {
// it also moves timers that have been modified to run later,
// and removes deleted timers. The caller must have locked ts.
func (ts *timers) adjust(now int64, force bool) {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
// If we haven't yet reached the time of the earliest modified
// timer, don't do anything. This speeds up programs that adjust
// a lot of timers back and forth if the timers rarely expire.
Expand Down Expand Up @@ -720,7 +728,7 @@ func (ts *timers) check(now int64) (rnow, pollUntil int64, ran bool) {
return now, next, false
}

lock(&ts.lock)
ts.lock()
if len(ts.heap) > 0 {
ts.adjust(now, force)
for len(ts.heap) > 0 {
Expand All @@ -734,8 +742,7 @@ func (ts *timers) check(now int64) (rnow, pollUntil int64, ran bool) {
ran = true
}
}

unlock(&ts.lock)
ts.unlock()

return now, pollUntil, ran
}
Expand All @@ -749,7 +756,7 @@ func (ts *timers) check(now int64) (rnow, pollUntil int64, ran bool) {
//
//go:systemstack
func (ts *timers) run(now int64) int64 {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
Redo:
if len(ts.heap) == 0 {
return -1
Expand Down Expand Up @@ -784,7 +791,7 @@ Redo:
}

ts.unlockAndRun(t, now, state, mp)
assertLockHeld(&ts.lock) // t is unlocked now, but not ts
assertLockHeld(&ts.mu) // t is unlocked now, but not ts
return 0
}

Expand All @@ -794,7 +801,7 @@ Redo:
//
//go:systemstack
func (ts *timers) unlockAndRun(t *timer, now int64, state uint32, mp *m) {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
if raceenabled {
tsLocal := &getg().m.p.ptr().timers
if tsLocal.raceCtx == 0 {
Expand Down Expand Up @@ -843,9 +850,9 @@ func (ts *timers) unlockAndRun(t *timer, now int64, state uint32, mp *m) {
gp.racectx = gp.m.p.ptr().timers.raceCtx
}

unlock(&ts.lock)
ts.unlock()
f(arg, seq)
lock(&ts.lock)
ts.lock()

if raceenabled {
gp := getg()
Expand Down Expand Up @@ -886,18 +893,18 @@ func updateTimerPMask(pp *p) {
// Looks like there are no timers, however another P
// may be adding one at this very moment.
// Take the lock to synchronize.
lock(&pp.timers.lock)
pp.timers.lock()
if len(pp.timers.heap) == 0 {
timerpMask.clear(pp.id)
}
unlock(&pp.timers.lock)
pp.timers.unlock()
}

// verifyTimerHeap verifies that the timers is in a valid state.
// This is only for debugging, and is only called if verifyTimers is true.
// The caller must have locked ts.
func (ts *timers) verify() {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
for i, t := range ts.heap {
if i == 0 {
// First timer has no parent.
Expand All @@ -920,7 +927,7 @@ func (ts *timers) verify() {
// updateMinWhen sets ts.minWhen to ts.heap[0].when.
// The caller must have locked ts.
func (ts *timers) updateMinWhen() {
assertLockHeld(&ts.lock)
assertLockHeld(&ts.mu)
if len(ts.heap) == 0 {
ts.minWhen.Store(0)
} else {
Expand Down

0 comments on commit c961ab3

Please sign in to comment.