nebula/mutex_debug.go

112 lines
2.7 KiB
Go

//go:build mutex_debug
// +build mutex_debug
package nebula
import (
"fmt"
"log"
"runtime"
"runtime/debug"
"sync"
"github.com/timandy/routine"
)
var threadLocal routine.ThreadLocal = routine.NewThreadLocalWithInitial(func() any { return map[mutexKey]mutexValue{} })
type mutexKeyType string
const (
mutexKeyTypeHostMap mutexKeyType = "hostmap"
mutexKeyTypeHostInfo = "hostinfo"
mutexKeyTypeHandshakeManager = "handshake-manager"
)
type mutexKey struct {
Type mutexKeyType
ID uint32
}
type mutexValue struct {
file string
line int
}
type syncRWMutex struct {
sync.RWMutex
mutexKey
}
func newSyncRWMutex(key mutexKey) syncRWMutex {
return syncRWMutex{
mutexKey: key,
}
}
func alertMutex(err error) {
log.Print(err, string(debug.Stack()))
}
func checkMutex(state map[mutexKey]mutexValue, add mutexKey) {
for k := range state {
if add == k {
alertMutex(fmt.Errorf("re-entrant lock: state=%v add=%v", state, add))
}
}
switch add.Type {
case mutexKeyTypeHostInfo:
// Check for any other hostinfo keys:
for k := range state {
if k.Type == mutexKeyTypeHostInfo {
alertMutex(fmt.Errorf("grabbing hostinfo lock and already have a hostinfo lock: state=%v add=%v", state, add))
}
}
if _, ok := state[mutexKey{Type: mutexKeyTypeHostMap}]; ok {
alertMutex(fmt.Errorf("grabbing hostinfo lock and already have hostmap: state=%v add=%v", state, add))
}
if _, ok := state[mutexKey{Type: mutexKeyTypeHandshakeManager}]; ok {
alertMutex(fmt.Errorf("grabbing hostinfo lock and already have handshake-manager: state=%v add=%v", state, add))
}
// case mutexKeyTypeHandshakeManager:
// if _, ok := state[mutexKey{Type: mutexKeyTypeHostMap}]; ok {
// alertMutex(fmt.Errorf("grabbing handshake-manager lock and already have hostmap: state=%v add=%v", state, add))
// }
case mutexKeyTypeHostMap:
if _, ok := state[mutexKey{Type: mutexKeyTypeHandshakeManager}]; ok {
alertMutex(fmt.Errorf("grabbing hostmap lock and already have handshake-manager: state=%v add=%v", state, add))
}
}
}
func (s *syncRWMutex) Lock() {
m := threadLocal.Get().(map[mutexKey]mutexValue)
checkMutex(m, s.mutexKey)
v := mutexValue{}
_, v.file, v.line, _ = runtime.Caller(1)
m[s.mutexKey] = v
s.RWMutex.Lock()
}
func (s *syncRWMutex) Unlock() {
m := threadLocal.Get().(map[mutexKey]mutexValue)
delete(m, s.mutexKey)
s.RWMutex.Unlock()
}
func (s *syncRWMutex) RLock() {
m := threadLocal.Get().(map[mutexKey]mutexValue)
checkMutex(m, s.mutexKey)
v := mutexValue{}
_, v.file, v.line, _ = runtime.Caller(1)
m[s.mutexKey] = v
s.RWMutex.RLock()
}
func (s *syncRWMutex) RUnlock() {
m := threadLocal.Get().(map[mutexKey]mutexValue)
delete(m, s.mutexKey)
s.RWMutex.RUnlock()
}