github.com/NebulousLabs/Sia@v1.3.7/sync/lock.go (about) 1 package sync 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 "sync" 8 "time" 9 ) 10 11 // RWMutex provides locking functions, and an ability to detect and remove 12 // deadlocks. 13 type RWMutex struct { 14 openLocks map[int]lockInfo 15 openLocksCounter int 16 openLocksMutex sync.Mutex 17 18 callDepth int 19 maxLockTime time.Duration 20 21 mu sync.RWMutex 22 } 23 24 // lockInfo contains information about when and how a lock call was made. 25 type lockInfo struct { 26 // When the lock was called. 27 lockTime time.Time 28 29 // Whether it was a RLock or a Lock. 30 read bool 31 32 // Call stack of the caller. 33 callingFiles []string 34 callingLines []int 35 } 36 37 // New takes a maxLockTime and returns a lock. The lock will never stay locked 38 // for more than maxLockTime, instead printing an error and unlocking after 39 // maxLockTime has passed. 40 func New(maxLockTime time.Duration, callDepth int) *RWMutex { 41 rwm := &RWMutex{ 42 openLocks: make(map[int]lockInfo), 43 maxLockTime: maxLockTime, 44 callDepth: callDepth, 45 } 46 47 go rwm.threadedDeadlockFinder() 48 49 return rwm 50 } 51 52 // threadedDeadlockFinder occasionally freezes the mutexes and scans all open mutexes, 53 // reporting any that have exceeded their time limit. 54 func (rwm *RWMutex) threadedDeadlockFinder() { 55 for { 56 rwm.openLocksMutex.Lock() 57 for id, info := range rwm.openLocks { 58 // Check if the lock has been held for longer than 'maxLockTime'. 59 if time.Now().Sub(info.lockTime) > rwm.maxLockTime { 60 str := fmt.Sprintf("A lock was held for too long, id '%v'. Call stack:\n", id) 61 for i := 0; i <= rwm.callDepth; i++ { 62 str += fmt.Sprintf("\tFile: '%v:%v'\n", info.callingFiles[i], info.callingLines[i]) 63 } 64 os.Stderr.WriteString(str) 65 os.Stderr.Sync() 66 67 // Undo the deadlock and delete the entry from the map. 68 if info.read { 69 rwm.mu.RUnlock() 70 } else { 71 rwm.mu.Unlock() 72 } 73 delete(rwm.openLocks, id) 74 } 75 } 76 rwm.openLocksMutex.Unlock() 77 78 time.Sleep(rwm.maxLockTime) 79 } 80 } 81 82 // safeLock is the generic function for doing safe locking. If the read flag is 83 // set, then a readlock will be used, otherwise a lock will be used. 84 func (rwm *RWMutex) safeLock(read bool) int { 85 // Get the call stack. 86 var li lockInfo 87 li.read = read 88 li.callingFiles = make([]string, rwm.callDepth+1) 89 li.callingLines = make([]int, rwm.callDepth+1) 90 for i := 0; i <= rwm.callDepth; i++ { 91 _, li.callingFiles[i], li.callingLines[i], _ = runtime.Caller(2 + i) 92 } 93 94 // Lock the mutex. 95 if read { 96 rwm.mu.RLock() 97 } else { 98 rwm.mu.Lock() 99 } 100 101 // Safely register that a lock has been triggered. 102 rwm.openLocksMutex.Lock() 103 li.lockTime = time.Now() 104 id := rwm.openLocksCounter 105 rwm.openLocks[id] = li 106 rwm.openLocksCounter++ 107 rwm.openLocksMutex.Unlock() 108 109 return id 110 } 111 112 // safeUnlock is the generic function for doing safe unlocking. If the lock had 113 // to be removed because a deadlock was detected, an error is printed. 114 func (rwm *RWMutex) safeUnlock(read bool, id int) { 115 rwm.openLocksMutex.Lock() 116 defer rwm.openLocksMutex.Unlock() 117 118 // Check if a deadlock has been detected and fixed manually. 119 _, exists := rwm.openLocks[id] 120 if !exists { 121 // Get the call stack. 122 callingFiles := make([]string, rwm.callDepth+1) 123 callingLines := make([]int, rwm.callDepth+1) 124 for i := 0; i <= rwm.callDepth; i++ { 125 _, callingFiles[i], callingLines[i], _ = runtime.Caller(2 + i) 126 } 127 128 fmt.Printf("A lock was held until deadlock, subsequent call to unlock failed. id '%v'. Call stack:\n", id) 129 for i := 0; i <= rwm.callDepth; i++ { 130 fmt.Printf("\tFile: '%v:%v'\n", callingFiles[i], callingLines[i]) 131 } 132 return 133 } 134 135 // Remove the lock and delete the entry from the map. 136 if read { 137 rwm.mu.RUnlock() 138 } else { 139 rwm.mu.Unlock() 140 } 141 delete(rwm.openLocks, id) 142 } 143 144 // RLock will read lock the RWMutex. The return value must be used as input 145 // when calling RUnlock. 146 func (rwm *RWMutex) RLock() int { 147 return rwm.safeLock(true) 148 } 149 150 // RUnlock will read unlock the RWMutex. The return value of calling RLock must 151 // be used as input. 152 func (rwm *RWMutex) RUnlock(id int) { 153 rwm.safeUnlock(true, id) 154 } 155 156 // Lock will lock the RWMutex. The return value must be used as input when 157 // calling RUnlock. 158 func (rwm *RWMutex) Lock() int { 159 return rwm.safeLock(false) 160 } 161 162 // Unlock will unlock the RWMutex. The return value of calling Lock must be 163 // used as input. 164 func (rwm *RWMutex) Unlock(id int) { 165 rwm.safeUnlock(false, id) 166 }