This repository has been archived by the owner on Apr 8, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Andrey Sibiryov
committed
Jan 26, 2017
1 parent
9dd1dcc
commit e3cf345
Showing
2 changed files
with
146 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package xsync | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"runtime" | ||
"sync" | ||
) | ||
|
||
const _StackDepth = 16 | ||
|
||
// DebugMutex is a RWMutex that tracks its ownership. | ||
type DebugMutex struct { | ||
m sync.RWMutex | ||
} | ||
|
||
// RLock locks DebugMutex for reading. | ||
func (m *DebugMutex) RLock() { m.m.RLock() } | ||
|
||
// RUnlock undoes a single RLock call. | ||
func (m *DebugMutex) RUnlock() { m.m.RUnlock() } | ||
|
||
// Lock locks DebugMutex for writing. | ||
func (m *DebugMutex) Lock() { | ||
m.m.Lock() | ||
insert(m) | ||
} | ||
|
||
// Unlock unlocks DebugMutex for writing. | ||
func (m *DebugMutex) Unlock() { | ||
remove(m) | ||
m.m.Unlock() | ||
} | ||
|
||
// RLocker returns a Locker interface implemented via calls to RLock | ||
// and RUnlock. | ||
func (m *DebugMutex) RLocker() sync.Locker { | ||
return (*rlocker)(m) | ||
} | ||
|
||
type rlocker DebugMutex | ||
|
||
func (r *rlocker) Lock() { r.m.RLock() } | ||
func (r *rlocker) Unlock() { r.m.RUnlock() } | ||
|
||
var mutexDebuggingFlag bool | ||
|
||
// DisableMutexDebugging turns mutex debugging off. | ||
func DisableMutexDebugging() { | ||
mutexDebuggingFlag = false | ||
} | ||
|
||
// EnableMutexDebugging turns mutex debugging on. | ||
func EnableMutexDebugging() { | ||
mutexDebuggingFlag = true | ||
} | ||
|
||
var activeLocks struct { | ||
sync.Mutex | ||
m map[*DebugMutex][]uintptr | ||
} | ||
|
||
func insert(m *DebugMutex) { | ||
if !mutexDebuggingFlag { | ||
return | ||
} | ||
|
||
r := make([]uintptr, _StackDepth) | ||
n := runtime.Callers(3, r) | ||
|
||
activeLocks.Lock() | ||
activeLocks.m[m] = r[:n] | ||
activeLocks.Unlock() | ||
} | ||
|
||
func remove(m *DebugMutex) { | ||
if !mutexDebuggingFlag { | ||
return | ||
} | ||
|
||
activeLocks.Lock() | ||
delete(activeLocks.m, m) | ||
activeLocks.Unlock() | ||
} | ||
|
||
func owner(l []uintptr) string { | ||
var ( | ||
b = new(bytes.Buffer) | ||
n runtime.Frame | ||
more = len(l) != 0 | ||
) | ||
|
||
for f := runtime.CallersFrames(l); more; { | ||
n, more = f.Next() | ||
fmt.Fprintf(b, "%s:%d\n\t%s\n", n.File, n.Line, n.Function) | ||
} | ||
|
||
return b.String() | ||
} | ||
|
||
// DumpOwned returns all currently locked mutexes. | ||
func DumpOwned() []string { | ||
var r []string | ||
|
||
activeLocks.Lock() | ||
|
||
for m, l := range activeLocks.m { | ||
r = append(r, fmt.Sprintf("mutex @ %p\n%s", m, owner(l))) | ||
} | ||
|
||
activeLocks.Unlock() | ||
|
||
return r | ||
} | ||
|
||
func init() { | ||
activeLocks.m = make(map[*DebugMutex][]uintptr) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package xsync | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestDebugMutex(t *testing.T) { | ||
EnableMutexDebugging() | ||
defer DisableMutexDebugging() | ||
|
||
m := &DebugMutex{} | ||
|
||
assert.Empty(t, DumpOwned()) | ||
|
||
m.Lock() | ||
assert.NotEmpty(t, DumpOwned()) | ||
|
||
m.Unlock() | ||
assert.Empty(t, DumpOwned()) | ||
|
||
m.RLock() | ||
assert.Empty(t, DumpOwned()) | ||
|
||
m.RUnlock() | ||
assert.Empty(t, DumpOwned()) | ||
} |