|
| 1 | +package sync |
| 2 | + |
| 3 | +import ( |
| 4 | + "bytes" |
| 5 | + "container/list" |
| 6 | + "fmt" |
| 7 | + "github.com/funny/goid" |
| 8 | + "runtime/pprof" |
| 9 | + "strconv" |
| 10 | + "sync" |
| 11 | + "sync/atomic" |
| 12 | +) |
| 13 | + |
| 14 | +var WatchDeadLock int32 = 1 |
| 15 | + |
| 16 | +var ( |
| 17 | + lockMutex = new(sync.Mutex) |
| 18 | + waitTargets = make(map[int32]*mutexInfo) |
| 19 | + goroutineBegin = []byte("goroutine ") |
| 20 | + goroutineEnd = []byte("\n\n") |
| 21 | +) |
| 22 | + |
| 23 | +func goroutine(id int32, stack []byte) []byte { |
| 24 | + head := append(strconv.AppendInt(goroutineBegin, int64(id), 10), ' ') |
| 25 | + begin := bytes.Index(stack, head) |
| 26 | + end := bytes.Index(stack[begin:], goroutineEnd) |
| 27 | + if end == -1 { |
| 28 | + end = len(stack) - begin |
| 29 | + } |
| 30 | + return stack[begin : begin+end] |
| 31 | +} |
| 32 | + |
| 33 | +type mutexInfo struct { |
| 34 | + holder int32 |
| 35 | + waiting *list.List |
| 36 | + watch bool |
| 37 | +} |
| 38 | + |
| 39 | +func (lock *mutexInfo) wait() (int32, *list.Element) { |
| 40 | + lockMutex.Lock() |
| 41 | + defer lockMutex.Unlock() |
| 42 | + |
| 43 | + holder := goid.Get() |
| 44 | + waitTargets[holder] = lock |
| 45 | + |
| 46 | + if lock.waiting == nil { |
| 47 | + lock.waiting = list.New() |
| 48 | + } |
| 49 | + |
| 50 | + lock.verify(holder, []*mutexInfo{lock}) |
| 51 | + |
| 52 | + return holder, lock.waiting.PushBack(holder) |
| 53 | +} |
| 54 | + |
| 55 | +func (lock *mutexInfo) verify(holder int32, link []*mutexInfo) { |
| 56 | + if lock.holder != 0 { |
| 57 | + if lock.holder == holder { |
| 58 | + stackBuf := new(bytes.Buffer) |
| 59 | + prof := pprof.Lookup("goroutine") |
| 60 | + prof.WriteTo(stackBuf, 2) |
| 61 | + stack := stackBuf.Bytes() |
| 62 | + |
| 63 | + buf := new(bytes.Buffer) |
| 64 | + fmt.Fprintln(buf, "[DEAD LOCK]\n") |
| 65 | + fmt.Fprintf(buf, "%s\n\n", goroutine(holder, stack)) |
| 66 | + for i := 0; i < len(link); i++ { |
| 67 | + fmt.Fprintf(buf, "%s\n\n", goroutine(link[i].holder, stack)) |
| 68 | + } |
| 69 | + panic(buf.String()) |
| 70 | + } |
| 71 | + if waitTarget, exists := waitTargets[lock.holder]; exists { |
| 72 | + waitTarget.verify(holder, append(link, waitTarget)) |
| 73 | + } |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +func (lock *mutexInfo) using(holder int32, elem *list.Element) { |
| 78 | + lockMutex.Lock() |
| 79 | + defer lockMutex.Unlock() |
| 80 | + |
| 81 | + delete(waitTargets, holder) |
| 82 | + |
| 83 | + atomic.StoreInt32(&lock.holder, holder) |
| 84 | + lock.waiting.Remove(elem) |
| 85 | +} |
| 86 | + |
| 87 | +func (lock *mutexInfo) release() { |
| 88 | + atomic.StoreInt32(&lock.holder, 0) |
| 89 | +} |
| 90 | + |
| 91 | +type Mutex struct { |
| 92 | + mutexInfo |
| 93 | + sync.Mutex |
| 94 | +} |
| 95 | + |
| 96 | +func (m *Mutex) Lock() { |
| 97 | + m.mutexInfo.watch = atomic.LoadInt32(&WatchDeadLock) == 1 |
| 98 | + |
| 99 | + if m.mutexInfo.watch { |
| 100 | + holder, elem := m.mutexInfo.wait() |
| 101 | + m.Mutex.Lock() |
| 102 | + m.mutexInfo.using(holder, elem) |
| 103 | + } else { |
| 104 | + m.Mutex.Lock() |
| 105 | + } |
| 106 | +} |
| 107 | + |
| 108 | +func (m *Mutex) Unlock() { |
| 109 | + if m.mutexInfo.watch { |
| 110 | + m.mutexInfo.release() |
| 111 | + } |
| 112 | + m.Mutex.Unlock() |
| 113 | +} |
0 commit comments