Skip to content

runtime: always acquire M when acquiring locks by rank #66276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/runtime/lockrank_off.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ func lockWithRank(l *mutex, rank lockRank) {
// This function may be called in nosplit context and thus must be nosplit.
//
//go:nosplit
func acquireLockRank(rank lockRank) {
func acquireLockRankAndM(rank lockRank) {
acquirem()
}

func unlockWithRank(l *mutex) {
Expand All @@ -37,7 +38,8 @@ func unlockWithRank(l *mutex) {
// This function may be called in nosplit context and thus must be nosplit.
//
//go:nosplit
func releaseLockRank(rank lockRank) {
func releaseLockRankAndM(rank lockRank) {
releasem(getg().m)
}

func lockWithRankMayAcquire(l *mutex, rank lockRank) {
Expand Down
16 changes: 12 additions & 4 deletions src/runtime/lockrank_on.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,16 @@ func printHeldLocks(gp *g) {
}
}

// acquireLockRank acquires a rank which is not associated with a mutex lock
// acquireLockRankAndM acquires a rank which is not associated with a mutex
// lock. To maintain the invariant that an M with m.locks==0 does not hold any
// lock-like resources, it also acquires the M.
//
// This function may be called in nosplit context and thus must be nosplit.
//
//go:nosplit
func acquireLockRank(rank lockRank) {
func acquireLockRankAndM(rank lockRank) {
acquirem()

gp := getg()
// Log the new class. See comment on lockWithRank.
systemstack(func() {
Expand Down Expand Up @@ -189,12 +193,14 @@ func unlockWithRank(l *mutex) {
})
}

// releaseLockRank releases a rank which is not associated with a mutex lock
// releaseLockRankAndM releases a rank which is not associated with a mutex
// lock. To maintain the invariant that an M with m.locks==0 does not hold any
// lock-like resources, it also releases the M.
//
// This function may be called in nosplit context and thus must be nosplit.
//
//go:nosplit
func releaseLockRank(rank lockRank) {
func releaseLockRankAndM(rank lockRank) {
gp := getg()
systemstack(func() {
found := false
Expand All @@ -211,6 +217,8 @@ func releaseLockRank(rank lockRank) {
throw("lockRank release without matching lockRank acquire")
}
})

releasem(getg().m)
}

// nosplit because it may be called from nosplit contexts.
Expand Down
9 changes: 4 additions & 5 deletions src/runtime/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,7 @@ func casfrom_Gscanstatus(gp *g, oldval, newval uint32) {
dumpgstatus(gp)
throw("casfrom_Gscanstatus: gp->status is not in scan state")
}
releaseLockRank(lockRankGscan)
releaseLockRankAndM(lockRankGscan)
}

// This will return false if the gp is not in the expected status and the cas fails.
Expand All @@ -1081,7 +1081,7 @@ func castogscanstatus(gp *g, oldval, newval uint32) bool {
if newval == oldval|_Gscan {
r := gp.atomicstatus.CompareAndSwap(oldval, newval)
if r {
acquireLockRank(lockRankGscan)
acquireLockRankAndM(lockRankGscan)
}
return r

Expand Down Expand Up @@ -1110,8 +1110,7 @@ func casgstatus(gp *g, oldval, newval uint32) {
})
}

acquireLockRank(lockRankGscan)
releaseLockRank(lockRankGscan)
lockWithRankMayAcquire(nil, lockRankGscan)

// See https://golang.org/cl/21503 for justification of the yield delay.
const yieldDelay = 5 * 1000
Expand Down Expand Up @@ -1245,7 +1244,7 @@ func casGToPreemptScan(gp *g, old, new uint32) {
if old != _Grunning || new != _Gscan|_Gpreempted {
throw("bad g transition")
}
acquireLockRank(lockRankGscan)
acquireLockRankAndM(lockRankGscan)
for !gp.atomicstatus.CompareAndSwap(_Grunning, _Gscan|_Gpreempted) {
}
}
Expand Down
7 changes: 2 additions & 5 deletions src/runtime/rwmutex.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ func (rw *rwmutex) rlock() {
// things blocking on the lock may consume all of the Ps and
// deadlock (issue #20903). Alternatively, we could drop the P
// while sleeping.
acquirem()

acquireLockRank(rw.readRank)
acquireLockRankAndM(rw.readRank)
lockWithRankMayAcquire(&rw.rLock, getLockRank(&rw.rLock))

if rw.readerCount.Add(1) < 0 {
Expand Down Expand Up @@ -116,8 +114,7 @@ func (rw *rwmutex) runlock() {
unlock(&rw.rLock)
}
}
releaseLockRank(rw.readRank)
releasem(getg().m)
releaseLockRankAndM(rw.readRank)
}

// lock locks rw for writing.
Expand Down