github.com/suiyunonghen/DxCommonLib@v0.5.3/sync/deadlock.go (about) 1 package sync 2 3 import ( 4 "github.com/suiyunonghen/DxCommonLib" 5 "github.com/suiyunonghen/DxCommonLib/system" 6 "runtime" 7 "strconv" 8 "strings" 9 "sync" 10 "sync/atomic" 11 "time" 12 ) 13 14 var bufferPool = sync.Pool{ 15 New: func() interface{} { 16 return make([]byte, 0, 256) 17 }, 18 } 19 20 type mutexStruct struct { 21 locking *LockInfo //当前锁定的信息 22 } 23 24 func (mutex mutexStruct)caller(offset int) (string, string) { 25 //0是当前位置,1是上一个位置Lock,2是调用点位置 26 pc, file, line, ok := runtime.Caller(2 + offset) 27 if !ok { 28 return "", "" 29 } 30 buffer := bufferPool.Get().([]byte) 31 idx := strings.LastIndexByte(file, '/') 32 if idx == -1 { 33 buffer = append(buffer[:0], file...) 34 } else { 35 idx = strings.LastIndexByte(file[:idx], '/') 36 if idx == -1 { 37 buffer = append(buffer[:0], file...) 38 } else { 39 buffer = append(buffer[:0], file[idx+1:]...) 40 } 41 } 42 funcName := runtime.FuncForPC(pc).Name() 43 buffer = append(buffer, ':') 44 buffer = strconv.AppendInt(buffer, int64(line), 10) 45 result := string(buffer) 46 buffer = buffer[:0] 47 bufferPool.Put(buffer) 48 idx = strings.IndexByte(funcName, '.') 49 if idx > 0 { 50 funcName = funcName[idx+1:] 51 } 52 return result, funcName 53 } 54 55 type LockStyle uint8 56 57 const( 58 LckUnLock LockStyle = iota 59 LckLockBlock //LockWait 60 LckLocking //Locking 61 ) 62 63 // 锁定信息 64 65 type LockInfo struct { 66 IsRLock bool //是否是RLock 67 LockStyle LockStyle 68 CheckIndex byte //检查了多少次 69 StartTime time.Time 70 GoRoutine int64 71 LockMsg string 72 Caller string //调用Lock的位置 73 CallerFunc string //调用Lock的函数 74 BeforeCaller string //前一步调用 75 BeforeFunc string //前一步调用 76 Owner *mutexStruct 77 } 78 79 var( 80 lockPool = sync.Pool{ 81 New: func() interface{}{ 82 return &LockInfo{} 83 }, 84 } 85 ) 86 87 func freeLockInfo(lckInfo *LockInfo) { 88 lckInfo.GoRoutine = 0 89 lckInfo.Owner.locking = nil 90 lckInfo.Owner = nil 91 lckInfo.Caller = "" 92 lckInfo.LockMsg = "" 93 lckInfo.CheckIndex = 0 94 lckInfo.CallerFunc = "" 95 lockPool.Put(lckInfo) 96 } 97 98 func (lckInfo *LockInfo)String()string { 99 if lckInfo.IsRLock{ 100 if lckInfo.LockStyle == LckUnLock{ 101 return lckInfo.Caller+"."+lckInfo.CallerFunc+".RUnlock" 102 } 103 return lckInfo.Caller+"."+lckInfo.CallerFunc+".RLock" 104 } 105 if lckInfo.LockStyle == LckUnLock{ 106 return lckInfo.Caller+"."+lckInfo.CallerFunc+".UnLock" 107 } 108 return lckInfo.Caller+"."+lckInfo.CallerFunc+".Lock" 109 } 110 111 // 扩展支持检查死锁 112 113 type RWMutexEx struct { 114 mutexStruct 115 sync.RWMutex 116 } 117 118 func (mutex *RWMutexEx)Lock() { 119 if atomic.LoadInt32(&deadCheck) == 0{ 120 mutex.RWMutex.Lock() 121 return 122 } 123 mutex.lock("",false) 124 } 125 126 func (mutex *RWMutexEx)lock(lockMsg string,isRlock bool) { 127 gid := system.GetRoutineId() 128 caller,method := mutex.caller(1) 129 before,beforeMethod := mutex.caller(2) //调用位置 130 lckInfo := lockPool.Get().(*LockInfo) 131 lckInfo.IsRLock = isRlock 132 lckInfo.LockStyle = LckLockBlock //lockWait 133 lckInfo.StartTime = time.Now() 134 lckInfo.GoRoutine = gid 135 lckInfo.LockMsg = lockMsg 136 lckInfo.Caller = caller 137 lckInfo.Owner = &mutex.mutexStruct 138 lckInfo.CallerFunc = method 139 lckInfo.BeforeFunc = beforeMethod 140 lckInfo.BeforeCaller = before 141 lockChan <- lckInfo 142 if isRlock{ 143 mutex.RWMutex.RLock() 144 }else{ 145 mutex.RWMutex.Lock() 146 } 147 lckInfo = lockPool.Get().(*LockInfo) 148 lckInfo.IsRLock = isRlock 149 lckInfo.LockStyle = LckLocking 150 lckInfo.StartTime = time.Now() 151 lckInfo.GoRoutine = gid 152 lckInfo.LockMsg = lockMsg 153 lckInfo.Caller = caller 154 lckInfo.Owner = &mutex.mutexStruct 155 lckInfo.CallerFunc = method 156 lckInfo.BeforeFunc = beforeMethod 157 lckInfo.BeforeCaller = before 158 lockChan <- lckInfo 159 } 160 161 func (mutex *RWMutexEx)LockWithMsg(lockMsg string) { 162 if atomic.LoadInt32(&deadCheck) == 0{ 163 mutex.RWMutex.Lock() 164 return 165 } 166 mutex.lock(lockMsg,false) 167 } 168 169 func (mutex *RWMutexEx)RLock() { 170 if atomic.LoadInt32(&deadCheck) == 0{ 171 mutex.RWMutex.RLock() 172 return 173 } 174 mutex.lock("",true) 175 } 176 177 func (mutex *RWMutexEx)RLockWithMsg(lockMsg string) { 178 if atomic.LoadInt32(&deadCheck) == 0{ 179 mutex.RWMutex.RLock() 180 return 181 } 182 mutex.lock(lockMsg,true) 183 } 184 185 func (mutex *RWMutexEx)Unlock() { 186 if atomic.LoadInt32(&deadCheck) == 0{ 187 mutex.RWMutex.Unlock() 188 return 189 } 190 gid := system.GetRoutineId() 191 caller,method := mutex.caller(0) 192 193 lckInfo := lockPool.Get().(*LockInfo) 194 lckInfo.IsRLock = false 195 lckInfo.LockStyle = LckUnLock 196 lckInfo.StartTime = time.Now() 197 lckInfo.GoRoutine = gid 198 lckInfo.Caller = caller 199 lckInfo.Owner = &mutex.mutexStruct 200 lckInfo.CallerFunc = method 201 lockChan <- lckInfo 202 mutex.RWMutex.Unlock() 203 } 204 205 func (mutex *RWMutexEx)RUnlock() { 206 if atomic.LoadInt32(&deadCheck) == 0{ 207 mutex.RWMutex.RUnlock() 208 return 209 } 210 gid := system.GetRoutineId() 211 caller,method := mutex.caller(0) 212 213 lckInfo := lockPool.Get().(*LockInfo) 214 lckInfo.IsRLock = true 215 lckInfo.LockStyle = LckUnLock 216 lckInfo.StartTime = time.Now() 217 lckInfo.GoRoutine = gid 218 lckInfo.Caller = caller 219 lckInfo.Owner = &mutex.mutexStruct 220 lckInfo.CallerFunc = method 221 lockChan <- lckInfo 222 mutex.RWMutex.RUnlock() 223 } 224 225 type MutexEx struct { 226 mutexStruct 227 sync.Mutex 228 } 229 230 func (mutex *MutexEx)Lock() { 231 if atomic.LoadInt32(&deadCheck) == 0{ 232 mutex.Mutex.Lock() 233 return 234 } 235 mutex.lock("") 236 } 237 238 func (mutex *MutexEx)LockWithMsg(lockMsg string) { 239 if atomic.LoadInt32(&deadCheck) == 0{ 240 mutex.Mutex.Lock() 241 return 242 } 243 mutex.lock(lockMsg) 244 } 245 246 func (mutex *MutexEx)lock(lockMsg string) { 247 //先获取当前的位置 248 gid := system.GetRoutineId() 249 caller,method := mutex.caller(1) 250 before,beforeMethod := mutex.caller(2) //调用位置 251 252 lckInfo := lockPool.Get().(*LockInfo) 253 lckInfo.IsRLock = false 254 lckInfo.LockStyle = LckLockBlock 255 lckInfo.StartTime = time.Now() 256 lckInfo.GoRoutine = gid 257 lckInfo.Caller = caller 258 lckInfo.LockMsg = lockMsg 259 lckInfo.Owner = &mutex.mutexStruct 260 lckInfo.CallerFunc = method 261 lckInfo.BeforeFunc = beforeMethod 262 lckInfo.BeforeCaller = before 263 lockChan <- lckInfo 264 265 mutex.Mutex.Lock() 266 267 lckInfo = lockPool.Get().(*LockInfo) 268 lckInfo.IsRLock = false 269 lckInfo.LockStyle = LckLocking 270 lckInfo.StartTime = time.Now() 271 lckInfo.GoRoutine = gid 272 lckInfo.LockMsg = lockMsg 273 lckInfo.Caller = caller 274 lckInfo.Owner = &mutex.mutexStruct 275 lckInfo.CallerFunc = method 276 lckInfo.BeforeFunc = beforeMethod 277 lckInfo.BeforeCaller = before 278 lockChan <- lckInfo 279 } 280 281 func (mutex *MutexEx)Unlock() { 282 if atomic.LoadInt32(&deadCheck) == 0{ 283 mutex.Mutex.Unlock() 284 return 285 } 286 gid := system.GetRoutineId() 287 caller,method := mutex.caller(0) 288 289 lckInfo := lockPool.Get().(*LockInfo) 290 lckInfo.IsRLock = false 291 lckInfo.LockStyle = LckUnLock 292 lckInfo.StartTime = time.Now() 293 lckInfo.GoRoutine = gid 294 lckInfo.Caller = caller 295 lckInfo.Owner = &mutex.mutexStruct 296 lckInfo.CallerFunc = method 297 lockChan <- lckInfo 298 mutex.Mutex.Unlock() 299 } 300 301 var lockChan chan *LockInfo 302 var deadCheck int32 303 var quitDeadCheck chan struct{} 304 305 306 func StartDeadCheck(lockNotify func(deadLockInfo []byte),checkInterval,maxLockInterval time.Duration,debugLog func(panicMsg bool, format string,data ...interface{})) { 307 if debugLog != nil{ 308 debugLog(false,"启动DeadLock检查") 309 } 310 atomic.StoreInt32(&deadCheck,1) 311 quitDeadCheck = make(chan struct{}) 312 go checkDeadLock(quitDeadCheck,checkInterval,maxLockInterval,lockNotify,debugLog) 313 } 314 315 func StopDeadCheck() { 316 atomic.StoreInt32(&deadCheck,0) 317 if quitDeadCheck != nil{ 318 close(quitDeadCheck) 319 quitDeadCheck = nil 320 } 321 } 322 323 func init() { 324 lockChan = make(chan *LockInfo,256) 325 } 326 327 func checkDeadLock(quit chan struct{},checkInterval,maxLockInterval time.Duration,lockNotify func(deadLockInfo []byte),debugLog func(panicMsg bool,format string,data ...interface{})) { 328 var lockWaits sync.Map //一般锁都会在全局用到,所以删除的概率比较小 329 locking := make([]*LockInfo,0,1024) 330 if checkInterval < time.Second * 5{ 331 checkInterval = time.Second * 5 332 } 333 buffer := make([]byte,0,2048) 334 willPanic := false 335 tk := DxCommonLib.After(checkInterval) 336 for{ 337 select{ 338 case <-quit: 339 return 340 case lckInfo := <-lockChan: 341 switch lckInfo.LockStyle { 342 case LckLockBlock: 343 var lockWait []*LockInfo 344 if wait,ok := lockWaits.Load(lckInfo.Owner);!ok{ 345 lockWait = make([]*LockInfo,0,1024) 346 }else{ 347 lockWait = wait.([]*LockInfo) 348 for i := range lockWait{ 349 if lockWait[i].GoRoutine == lckInfo.GoRoutine{ 350 //同一个goroutine多次执行锁定 351 if debugLog != nil{ 352 debugLog(true,"同一goroutine锁定1:%s,当前重入锁定2:%s",lockWait[i].String(),lckInfo.String()) 353 } 354 return 355 } 356 } 357 } 358 lockWait = append(lockWait,lckInfo) 359 lockWaits.Store(lckInfo.Owner,lockWait) 360 case LckLocking: 361 //将上次的LockWait的释放掉 362 if wait,ok := lockWaits.Load(lckInfo.Owner);ok{ 363 var lastLock *LockInfo 364 lockWait := wait.([]*LockInfo) 365 l := len(lockWait) 366 for i := 0;i < l;i++{ 367 lastLock = lockWait[i] 368 if lastLock.GoRoutine == lckInfo.GoRoutine{ 369 lockWait[i] = nil 370 lockPool.Put(lastLock) 371 if i == l - 1{ 372 lockWait = lockWait[:l-1] 373 }else{ 374 lockWait = append(lockWait[:i],lockWait[i+1:]...) 375 } 376 lockWaits.Store(lckInfo.Owner,lockWait) 377 break 378 } 379 } 380 lckInfo.Owner.locking = lckInfo 381 locking = append(locking,lckInfo) 382 }else{ 383 freeLockInfo(lckInfo) 384 } 385 case LckUnLock: 386 //释放Locking,一般Lock和UnLock成对出现,都是在同一个goroutine中,所以 goroutine是一致的 387 l := len(locking) 388 var foundLock *LockInfo 389 for i := 0;i < l;i++{ 390 if locking[i].GoRoutine == lckInfo.GoRoutine && locking[i].Owner == lckInfo.Owner && locking[i].IsRLock == lckInfo.IsRLock{ 391 foundLock = locking[i] 392 locking[i] = nil 393 freeLockInfo(lckInfo) 394 if i == l - 1{ 395 locking = locking[:l-1] 396 }else{ 397 locking = append(locking[:i],locking[i+1:]...) 398 } 399 break 400 } 401 } 402 if foundLock == nil && debugLog != nil{ 403 debugLog(true,"未找到在同一goroutine的成对释放%s",lckInfo.String()) 404 } 405 } 406 case <-tk: 407 //检查一次锁信息 408 buffer = buffer[:0] 409 now := time.Now() 410 for i := range locking{ 411 lockTimes := now.Sub(locking[i].StartTime) 412 if lockTimes >= checkInterval{ 413 willPanic = lockTimes >= maxLockInterval 414 locking[i].CheckIndex = locking[i].CheckIndex + 1 415 buffer = append(buffer,"触发次数:"...) 416 buffer = strconv.AppendInt(buffer,int64(locking[i].CheckIndex),10) 417 buffer = append(buffer,",锁定时长:"...) 418 buffer = append(buffer,lockTimes.String()...) 419 buffer = append(buffer,",GoRoutine:"...) 420 buffer = strconv.AppendInt(buffer,locking[i].GoRoutine,10) 421 buffer = append(buffer,",锁定位置:"...) 422 buffer = append(buffer,locking[i].Caller...) 423 buffer = append(buffer,'.') 424 buffer = append(buffer,locking[i].CallerFunc...) 425 buffer = append(buffer," from "...) 426 buffer = append(buffer,locking[i].BeforeCaller...) 427 buffer = append(buffer,'.') 428 buffer = append(buffer,locking[i].BeforeFunc...) 429 430 buffer = append(buffer,",调用消息:"...) 431 buffer = append(buffer,locking[i].LockMsg...) 432 if waits,ok := lockWaits.Load(locking[i].Owner);ok{ 433 buffer = append(buffer,"\r\n堵塞内容:\r\n"...) 434 lockWait := waits.([]*LockInfo) 435 for waitIndex := range lockWait{ 436 buffer = append(buffer,"堵塞时长:"...) 437 buffer = append(buffer,now.Sub(lockWait[waitIndex].StartTime).String()...) 438 buffer = append(buffer,",GoRoutine:"...) 439 buffer = strconv.AppendInt(buffer,lockWait[waitIndex].GoRoutine,10) 440 buffer = append(buffer,",等待位置:"...) 441 buffer = append(buffer,lockWait[waitIndex].Caller...) 442 buffer = append(buffer,'.') 443 buffer = append(buffer,lockWait[waitIndex].CallerFunc...) 444 445 buffer = append(buffer," from "...) 446 buffer = append(buffer,lockWait[waitIndex].BeforeCaller...) 447 buffer = append(buffer,'.') 448 buffer = append(buffer,lockWait[waitIndex].BeforeFunc...) 449 450 buffer = append(buffer,",调用消息:"...) 451 buffer = append(buffer,lockWait[waitIndex].LockMsg...) 452 buffer = append(buffer,[]byte{'\r','\n'}...) 453 } 454 } 455 } 456 } 457 if willPanic{ 458 if debugLog != nil{ 459 debugLog(true,DxCommonLib.FastByte2String(buffer)) 460 return 461 } 462 panic(DxCommonLib.FastByte2String(buffer)) 463 } 464 if len(buffer) > 0{ 465 if lockNotify != nil{ 466 lockNotify(buffer) 467 }else if debugLog != nil{ 468 debugLog(false,DxCommonLib.FastByte2String(buffer)) 469 } 470 } 471 tk = DxCommonLib.After(checkInterval) 472 } 473 } 474 }