github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/trackedlock/api.go (about) 1 package trackedlock 2 3 import ( 4 "fmt" 5 "sync" 6 "sync/atomic" 7 8 "github.com/swiftstack/ProxyFS/logger" 9 ) 10 11 /* 12 13 * The trackedlock packages provides an implementation of sync.Mutex and 14 * sync.RWMutex interfaces that provides additional functionality in the form of 15 * lock hold tracking. In addition, the trackedlock.RWMutexTrack routines can 16 * be used to track other RWMutex-like synchronization primitives, like 17 * dlm.RWLockStruct and chunked put connections. 18 * 19 * Specifically, if lock tracking is enabled, the trackedlock packages checks 20 * the lock hold time. When a lock unlocked, if it was held longer than 21 * "LockHoldTimeLimit" then a warning is logged along with the stack trace of 22 * the Lock() and Unlock() of the lock. In addition, a daemon, the trackedlock 23 * watcher, periodically checks to see if any lock has been locked too long. 24 * When a lock is held too long, the daemon logs the goroutine ID and the stack 25 * trace of the goroutine that acquired the lock. 26 * 27 * Subsequent versions of this package may include lock heirarchy checking. 28 * 29 * The config variable "TrackedLock.LockHoldTimeLimit" is the hold time that 30 * triggers warning messages being logged. If it is 0 then locks are not 31 * tracked and the overhead of this package is minimal. 32 * 33 * The config variable "TrackedLock.LockCheckPeriod" is how often the daemon 34 * checks tracked locks. If it is 0 then no daemon is created and lock hold 35 * time is checked only when the lock is unlocked (assuming it is unlocked). 36 * 37 * trackedlock locks can be locked before this package is initialized, but they 38 * will not be tracked until the first time they are locked after 39 * initializaiton. 40 * 41 * The API consists of the config based trackedlock.Up() / Down() / 42 * PauseAndContract() / ExpandAndResume() (which are not defined here) and then 43 * the Mutex, RWMutex, and RWMutexTrack interfaces. 44 */ 45 46 // The Mutex type that we export, which wraps sync.Mutex to add tracking of lock 47 // hold time and the stack trace of the locker. 48 // 49 type Mutex struct { 50 wrappedMutex sync.Mutex // the actual Mutex 51 tracker MutexTrack // tracking information for the Mutex 52 } 53 54 // The RWMutex type that we export, which wraps sync.RWMutex to add tracking of 55 // lock hold time and the stack trace of the locker. 56 // 57 type RWMutex struct { 58 wrappedRWMutex sync.RWMutex // actual Mutex 59 rwTracker RWMutexTrack // track holds in shared (reader) mode 60 } 61 62 // 63 // Tracked Mutex API 64 // 65 func (m *Mutex) Lock() { 66 m.wrappedMutex.Lock() 67 68 m.tracker.lockTrack(m, nil) 69 } 70 71 func (m *Mutex) Unlock() { 72 m.tracker.unlockTrack(m) 73 74 m.wrappedMutex.Unlock() 75 } 76 77 // 78 // Tracked RWMutex API 79 // 80 func (m *RWMutex) Lock() { 81 m.wrappedRWMutex.Lock() 82 83 m.rwTracker.lockTrack(m) 84 } 85 86 func (m *RWMutex) Unlock() { 87 m.rwTracker.unlockTrack(m) 88 89 m.wrappedRWMutex.Unlock() 90 } 91 92 func (m *RWMutex) RLock() { 93 m.wrappedRWMutex.RLock() 94 95 m.rwTracker.rLockTrack(m) 96 } 97 98 func (m *RWMutex) RUnlock() { 99 m.rwTracker.rUnlockTrack(m) 100 101 m.wrappedRWMutex.RUnlock() 102 } 103 104 // 105 // Direct access to trackedlock API for DLM locks 106 // 107 func (rwmt *RWMutexTrack) LockTrack(lck interface{}) { 108 rwmt.lockTrack(lck) 109 } 110 111 func (rwmt *RWMutexTrack) UnlockTrack(lck interface{}) { 112 rwmt.unlockTrack(lck) 113 } 114 115 func (rwmt *RWMutexTrack) RLockTrack(lck interface{}) { 116 rwmt.rLockTrack(lck) 117 } 118 119 func (rwmt *RWMutexTrack) RUnlockTrack(lck interface{}) { 120 rwmt.rUnlockTrack(lck) 121 } 122 123 func (rwmt *RWMutexTrack) DLMUnlockTrack(lck interface{}) { 124 // This uses m.tracker.lockCnt without holding the mutex that protects it. 125 // Because this goroutine holds the lock, it cannot change from -1 to 0 126 // or >0 to 0 (unless there's a bug where another goroutine releases the 127 // lock). It can change from, say, 1 to 2 or 4 to 3, but that's benign 128 // (let's hope the race detector doesn't complain). 129 lockCnt := atomic.LoadInt32(&rwmt.tracker.lockCnt) 130 switch { 131 case lockCnt == -1: 132 rwmt.unlockTrack(lck) 133 case lockCnt > 0: 134 rwmt.rUnlockTrack(lck) 135 default: 136 errstring := fmt.Errorf("tracker for RWMutexTrack has illegal lockCnt %d", lockCnt) 137 logger.PanicfWithError(errstring, "%T lock at %p: %+v", lck, lck, lck) 138 } 139 return 140 }