// Copyright 2021, Chef. All rights reserved. // https://github.com/q191201771/naza // // Use of this source code is governed by a MIT-style license // that can be found in the License file. // // Author: Chef (191201771@qq.com) package nazasync import ( "fmt" "strings" "sync" "time" "github.com/q191201771/naza/pkg/unique" "github.com/q191201771/naza/pkg/nazalog" ) // 用于debug锁方面的问题 var uniqueGen *unique.SingleGenerator type Mutex struct { core sync.Mutex startHoldTime time.Time genUniqueKeyOnce sync.Once uniqueKey string } func (m *Mutex) Lock() { gid, _ := CurGoroutineId() m.genUniqueKeyOnce.Do(func() { m.uniqueKey = uniqueGen.GenUniqueKey() }) b := time.Now() nazalog.Out(nazalog.LevelDebug, 3, fmt.Sprintf("[%s] > Lock(). gid=%d", m.uniqueKey, gid)) globalMutexManager.beforeAcquireLock(m.uniqueKey, gid) m.core.Lock() globalMutexManager.afterAcquireLock(m.uniqueKey, gid) m.startHoldTime = time.Now() nazalog.Out(nazalog.LevelDebug, 3, fmt.Sprintf("[%s] < Lock(). gid=%d, acquire cost=%dms", m.uniqueKey, gid, time.Now().Sub(b).Milliseconds())) } func (m *Mutex) Unlock() { gid, _ := CurGoroutineId() // 运行时自己会检查对没有加锁的mutex进行Unlock调用的情况 b := time.Now() nazalog.Out(nazalog.LevelDebug, 3, fmt.Sprintf("[%s] > Unlock(). gid=%d, hold cost=%dms", m.uniqueKey, gid, time.Now().Sub(m.startHoldTime).Milliseconds())) m.core.Unlock() globalMutexManager.afterUnlock(m.uniqueKey, gid) nazalog.Out(nazalog.LevelDebug, 3, fmt.Sprintf("[%s] < Unlock(). gid=%d, release cost=%dms", m.uniqueKey, gid, time.Now().Sub(b).Milliseconds())) } var globalMutexManager = NewMutexManager() // 注意,key是由mutex唯一ID加上协程ID组合而成 type MutexManager struct { mu sync.Mutex waitAcquireContainer map[string]time.Time holdContainer map[string]time.Time } func NewMutexManager() *MutexManager { m := &MutexManager{ waitAcquireContainer: make(map[string]time.Time), holdContainer: make(map[string]time.Time), } go m.printTmpDebug() return m } func (m *MutexManager) printTmpDebug() { var buf strings.Builder for { m.mu.Lock() now := time.Now() buf.Reset() buf.WriteString("long wait acquire:") for k, t := range m.waitAcquireContainer { duration := now.Sub(t).Milliseconds() if duration > 1000 { buf.WriteString(fmt.Sprintf(" (%s:%dms)", k, duration)) } } nazalog.Out(nazalog.LevelDebug, 4, buf.String()) buf.Reset() buf.WriteString("long hold:") for k, t := range m.holdContainer { duration := now.Sub(t).Milliseconds() if duration > 1000 { buf.WriteString(fmt.Sprintf(" (%s:%dms)", k, duration)) } } nazalog.Out(nazalog.LevelDebug, 4, buf.String()) m.mu.Unlock() time.Sleep(5 * time.Second) } } func (m *MutexManager) beforeAcquireLock(uk string, gid int64) { k := fmt.Sprintf("%s_%d", uk, gid) m.mu.Lock() defer m.mu.Unlock() if _, exist := m.waitAcquireContainer[k]; exist { nazalog.Out(nazalog.LevelError, 4, fmt.Sprintf("[%s] one g try acquire lock twice(wait acquire already). gid=%d", uk, gid)) } // 当前协程已持有锁,再次重入 if _, exist := m.holdContainer[k]; exist { nazalog.Out(nazalog.LevelError, 4, fmt.Sprintf("[%s] one g try acquire lock twice(hold already). gid=%d", uk, gid)) } m.waitAcquireContainer[k] = time.Now() } func (m *MutexManager) afterAcquireLock(uk string, gid int64) { k := fmt.Sprintf("%s_%d", uk, gid) m.mu.Lock() defer m.mu.Unlock() if _, exist := m.waitAcquireContainer[k]; !exist { nazalog.Out(nazalog.LevelError, 4, fmt.Sprintf("[%s] acquired but not in wait acquire container. gid=%d", uk, gid)) } if _, exist := m.holdContainer[k]; exist { nazalog.Out(nazalog.LevelError, 4, fmt.Sprintf("[%s] acquired but in hold container already. gid=%d", uk, gid)) } delete(m.waitAcquireContainer, k) m.holdContainer[k] = time.Now() } func (m *MutexManager) afterUnlock(uk string, gid int64) { k := fmt.Sprintf("%s_%d", uk, gid) m.mu.Lock() defer m.mu.Unlock() // 有可能是a协程Lock,b协程Unlock if _, exist := m.holdContainer[k]; !exist { nazalog.Out(nazalog.LevelError, 4, fmt.Sprintf("[%s] unlock but not in hold container. gid=%d", uk, gid)) } if _, exist := m.waitAcquireContainer[k]; exist { nazalog.Out(nazalog.LevelError, 4, fmt.Sprintf("[%s] unlock but in wait acquire container already. gid=%d", uk, gid)) } delete(m.holdContainer, k) } func init() { uniqueGen = unique.NewSingleGenerator("MUTEX") }