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  }