Source File
rwmutex.go
Belonging Package
runtime
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
)
// This is a copy of sync/rwmutex.go rewritten to work in the runtime.
// A rwmutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers or a single writer.
// This is a variant of sync.RWMutex, for the runtime package.
// Like mutex, rwmutex blocks the calling M.
// It does not interact with the goroutine scheduler.
type rwmutex struct {
rLock mutex // protects readers, readerPass, writer
readers muintptr // list of pending readers
readerPass uint32 // number of pending readers to skip readers list
wLock mutex // serializes writers
writer muintptr // pending writer waiting for completing readers
readerCount atomic.Int32 // number of pending readers
readerWait atomic.Int32 // number of departing readers
}
const rwmutexMaxReaders = 1 << 30
// rlock locks rw for reading.
func ( *rwmutex) () {
// The reader must not be allowed to lose its P or else other
// things blocking on the lock may consume all of the Ps and
// deadlock (issue #20903). Alternatively, we could drop the P
// while sleeping.
acquirem()
if .readerCount.Add(1) < 0 {
// A writer is pending. Park on the reader queue.
systemstack(func() {
lockWithRank(&.rLock, lockRankRwmutexR)
if .readerPass > 0 {
// Writer finished.
.readerPass -= 1
unlock(&.rLock)
} else {
// Queue this reader to be woken by
// the writer.
:= getg().m
.schedlink = .readers
.readers.set()
unlock(&.rLock)
notesleep(&.park)
noteclear(&.park)
}
})
}
}
// runlock undoes a single rlock call on rw.
func ( *rwmutex) () {
if := .readerCount.Add(-1); < 0 {
if +1 == 0 || +1 == -rwmutexMaxReaders {
throw("runlock of unlocked rwmutex")
}
// A writer is pending.
if .readerWait.Add(-1) == 0 {
// The last reader unblocks the writer.
lockWithRank(&.rLock, lockRankRwmutexR)
:= .writer.ptr()
if != nil {
notewakeup(&.park)
}
unlock(&.rLock)
}
}
releasem(getg().m)
}
// lock locks rw for writing.
func ( *rwmutex) () {
// Resolve competition with other writers and stick to our P.
lockWithRank(&.wLock, lockRankRwmutexW)
:= getg().m
// Announce that there is a pending writer.
:= .readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for any active readers to complete.
lockWithRank(&.rLock, lockRankRwmutexR)
if != 0 && .readerWait.Add() != 0 {
// Wait for reader to wake us up.
systemstack(func() {
.writer.set()
unlock(&.rLock)
notesleep(&.park)
noteclear(&.park)
})
} else {
unlock(&.rLock)
}
}
// unlock unlocks rw for writing.
func ( *rwmutex) () {
// Announce to readers that there is no active writer.
:= .readerCount.Add(rwmutexMaxReaders)
if >= rwmutexMaxReaders {
throw("unlock of unlocked rwmutex")
}
// Unblock blocked readers.
lockWithRank(&.rLock, lockRankRwmutexR)
for .readers.ptr() != nil {
:= .readers.ptr()
.readers = .schedlink
.schedlink.set(nil)
notewakeup(&.park)
-= 1
}
// If r > 0, there are pending readers that aren't on the
// queue. Tell them to skip waiting.
.readerPass += uint32()
unlock(&.rLock)
// Allow other writers to proceed.
unlock(&.wLock)
}
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |