forked from RobinUS2/golang-mutex-tracer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lock.go
93 lines (77 loc) · 2.15 KB
/
lock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package muxtracer
import (
"sync"
"sync/atomic"
)
type Mutex struct {
lock sync.Mutex
// internal trace fields
threshold atomic.Uint64 // 0 when disabled, else threshold in nanoseconds
beginAwaitLock atomic.Uint64 // start time in unix nanoseconds from start waiting for lock
beginAwaitUnlock atomic.Uint64 // start time in unix nanoseconds from start waiting for unlock
lockObtained atomic.Uint64 // once we've entered the lock in unix nanoseconds
id []byte // if set this will be printed as string
}
func (m *Mutex) Lock() {
tracingThreshold := m.isTracing()
if tracingThreshold != 0 {
m.traceBeginAwaitLock()
}
// actual lock
m.lock.Lock()
if tracingThreshold != 0 {
m.traceEndAwaitLock(tracingThreshold)
}
}
func (m *Mutex) Unlock() {
tracingThreshold := m.isTracing()
if tracingThreshold != 0 {
m.traceBeginAwaitUnlock()
}
// unlock
m.lock.Unlock()
if tracingThreshold != 0 {
m.traceEndAwaitUnlock(tracingThreshold)
}
}
func (m *Mutex) isTracing() Threshold {
tracingThreshold := m.threshold.Load()
if tracingThreshold == 0 {
// always on?
tracingThreshold = defaultThreshold.Load()
}
return Threshold(tracingThreshold)
}
func (m *Mutex) traceBeginAwaitLock() {
m.beginAwaitLock.Store(now())
}
func (m *Mutex) traceEndAwaitLock(threshold Threshold) {
ts := now() // first obtain the current time
start := m.beginAwaitLock.Load()
m.lockObtained.Store(ts)
var took uint64
if start < ts {
// check for no overflow
took = ts - start
}
if took >= uint64(threshold) {
logViolation(Id(m.id), threshold, Actual(took), Now(ts), ViolationLock)
}
}
func (m *Mutex) traceBeginAwaitUnlock() {
m.beginAwaitUnlock.Store(now())
}
func (m *Mutex) traceEndAwaitUnlock(threshold Threshold) {
ts := now() // first obtain the current time
// lock obtained time (critical section)
lockObtained := m.lockObtained.Load()
var took uint64
if lockObtained < ts {
// check for no overflow
took = ts - lockObtained
}
if took >= uint64(threshold) && lockObtained > 0 {
// lockObtained = 0 when the tracer is enabled half way
logViolation(Id(m.id), threshold, Actual(took), Now(ts), ViolationCritical)
}
}