internal/locker/rw_locker.go (75 lines of code) (raw):
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Provides the RWLocker implementations with optional debug utils.
package locker
import (
"runtime"
"sync"
"time"
"github.com/googlecloudplatform/gcsfuse/v2/internal/logger"
)
type RWLocker interface {
sync.Locker
RLock()
RUnlock()
}
// NewRW returns a RW locker with potential capability for debugging.
//
// Note: The deadlock detection is done only for writer lock and not for reader
// lock.
func NewRW(name string, check func()) RWLocker {
var l RWLocker = &sync.RWMutex{}
if gEnableInvariantsCheck {
l = &rwChecker{
locker: l,
check: check,
}
}
if gEnableDebugMessages {
l = &rwDebugger{
locker: l,
name: name,
}
}
return l
}
type rwChecker struct {
locker RWLocker
check func()
}
func (c *rwChecker) Lock() {
c.locker.Lock()
c.check()
}
func (c *rwChecker) Unlock() {
c.check()
c.locker.Unlock()
}
func (c *rwChecker) RLock() {
c.locker.RLock()
c.check()
}
func (c *rwChecker) RUnlock() {
c.check()
c.locker.RUnlock()
}
// Note: rwDebugger doesn't check potential deadlock in case of read only lock as
// doing that is not straight forward because multiple readers can acquire locks
// at the same time and that needs keeping track of every different lock.
type rwDebugger struct {
locker RWLocker
name string
holder string
timer *time.Timer
}
func (d *rwDebugger) Lock() {
d.locker.Lock()
buf := make([]byte, 2048)
runtime.Stack(buf, false /* all */)
d.holder = string(buf)
d.timer = time.AfterFunc(5*time.Second, func() {
logger.Tracef("debug_mutex: Potential dead lock detected for a lock %q held by: %v\n", d.name, d.holder)
})
}
func (d *rwDebugger) Unlock() {
d.holder = ""
d.timer.Stop()
d.timer = nil
d.locker.Unlock()
}
func (d *rwDebugger) RLock() {
d.locker.RLock()
}
func (d *rwDebugger) RUnlock() {
d.locker.RUnlock()
}