Skip to content

Commit fecc21c

Browse files
committed
refactor
1 parent a4de3bc commit fecc21c

File tree

3 files changed

+115
-123
lines changed

3 files changed

+115
-123
lines changed

base_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package sync
22

33
import (
4+
"strconv"
45
"sync"
56
"testing"
67
)
@@ -20,3 +21,32 @@ func Benchmark_Lock2(b *testing.B) {
2021
mutex.Unlock()
2122
}
2223
}
24+
25+
func Test_NoDeadlock(t *testing.T) {
26+
var (
27+
mutex Mutex
28+
wait WaitGroup
29+
)
30+
31+
wait.Add(1)
32+
go func() {
33+
for i := 0; i < 10000; i++ {
34+
mutex.Lock()
35+
strconv.Itoa(i)
36+
mutex.Unlock()
37+
}
38+
wait.Done()
39+
}()
40+
41+
wait.Add(1)
42+
go func() {
43+
for i := 0; i < 10000; i++ {
44+
mutex.Lock()
45+
strconv.Itoa(i)
46+
mutex.Unlock()
47+
}
48+
wait.Done()
49+
}()
50+
51+
wait.Wait()
52+
}

deadlock.go

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ package sync
44

55
import (
66
"bytes"
7+
"github.com/funny/debug"
78
"github.com/funny/goid"
8-
"runtime/pprof"
99
"strconv"
1010
"sync"
11-
"sync/atomic"
1211
)
1312

1413
type Mutex struct {
@@ -17,9 +16,9 @@ type Mutex struct {
1716
}
1817

1918
func (m *Mutex) Lock() {
20-
holder := m.mointor.wait()
19+
holder, holderStack := m.mointor.wait()
2120
m.Mutex.Lock()
22-
m.mointor.using(holder)
21+
m.mointor.using(holder, holderStack)
2322
}
2423

2524
func (m *Mutex) Unlock() {
@@ -51,70 +50,58 @@ var (
5150
globalMutex = new(sync.Mutex)
5251
waitTargets = make(map[int32]*mointor)
5352
goroutineBegin = []byte("goroutine ")
54-
goroutineEnd = []byte("\n\n")
53+
newline = []byte{'\n'}
5554
)
5655

5756
type mointor struct {
58-
holder int32
57+
holder int32
58+
holderStack debug.StackInfo
5959
}
6060

61-
func (m *mointor) wait() int32 {
61+
func (m *mointor) wait() (int32, debug.StackInfo) {
6262
globalMutex.Lock()
6363
defer globalMutex.Unlock()
6464

6565
holder := goid.Get()
66+
holderStack := debug.StackTrace(3, 0)
6667
waitTargets[holder] = m
6768

68-
m.verify(holder, []int32{holder})
69-
return holder
69+
m.verify([]*mointor{{holder, holderStack}})
70+
71+
return holder, holderStack
7072
}
7173

72-
func (m *mointor) verify(holder int32, holderLink []int32) {
74+
func (m *mointor) verify(holderLink []*mointor) {
7375
if m.holder != 0 {
7476
// deadlock detected
75-
if m.holder == holder {
76-
// dump stack
77-
stackBuf := new(bytes.Buffer)
78-
prof := pprof.Lookup("goroutine")
79-
prof.WriteTo(stackBuf, 2)
80-
stack := stackBuf.Bytes()
81-
82-
// match goroutines
77+
if m.holder == holderLink[0].holder {
8378
buf := new(bytes.Buffer)
8479
buf.WriteString("[DEAD LOCK]\n")
85-
buf.Write(traceGoroutine(holder, stack))
86-
buf.Write(goroutineEnd)
8780
for i := 0; i < len(holderLink); i++ {
88-
buf.Write(traceGoroutine(holderLink[i], stack))
89-
buf.Write(goroutineEnd)
81+
buf.Write(goroutineBegin)
82+
buf.WriteString(strconv.Itoa(int(holderLink[i].holder)))
83+
buf.Write(newline)
84+
buf.Write(holderLink[i].holderStack.Bytes(" "))
9085
}
9186
panic(DeadlockError(buf.String()))
9287
}
9388
// the lock holder is waiting for another lock
9489
if waitTarget, exists := waitTargets[m.holder]; exists {
95-
waitTarget.verify(holder, append(holderLink, m.holder))
90+
waitTarget.verify(append(holderLink, m))
9691
}
9792
}
9893
}
9994

100-
func (m *mointor) using(holder int32) {
95+
func (m *mointor) using(holder int32, holderStack debug.StackInfo) {
10196
globalMutex.Lock()
10297
defer globalMutex.Unlock()
10398

10499
delete(waitTargets, holder)
105-
atomic.StoreInt32(&m.holder, holder)
100+
m.holder = holder
101+
m.holderStack = holderStack
106102
}
107103

108104
func (m *mointor) release() {
109-
atomic.StoreInt32(&m.holder, 0)
110-
}
111-
112-
func traceGoroutine(id int32, stack []byte) []byte {
113-
head := append(strconv.AppendInt(goroutineBegin, int64(id), 10), ' ')
114-
begin := bytes.Index(stack, head)
115-
end := bytes.Index(stack[begin:], goroutineEnd)
116-
if end == -1 {
117-
end = len(stack) - begin
118-
}
119-
return stack[begin : begin+end]
105+
m.holder = 0
106+
m.holderStack = nil
120107
}

deadlock_test.go

Lines changed: 62 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -15,119 +15,94 @@ func init() {
1515
}()
1616
}
1717

18-
func testRecover(err interface{}, testDone chan int) {
19-
if err != nil {
20-
switch err.(type) {
21-
case DeadlockError:
22-
if testing.Verbose() {
23-
println(err.(DeadlockError).Error())
24-
}
25-
testDone <- 1
26-
default:
27-
panic(err)
28-
}
29-
}
30-
}
31-
32-
func Test_DeadLock1(t *testing.T) {
33-
testDone := make(chan int)
34-
35-
var mutex1 Mutex
18+
func deadlockTest(t *testing.T, callback func()) {
19+
testDone := make(chan interface{})
3620

3721
go func() {
3822
defer func() {
39-
err := recover()
40-
testRecover(err, testDone)
23+
testDone <- recover()
4124
}()
42-
43-
mutex1.Lock()
44-
mutex1.Lock()
25+
callback()
4526
}()
4627

4728
select {
48-
case <-testDone:
29+
case err := <-testDone:
30+
if err != nil {
31+
switch err.(type) {
32+
case DeadlockError:
33+
if testing.Verbose() {
34+
println(err.(DeadlockError).Error())
35+
}
36+
default:
37+
panic(err)
38+
}
39+
}
4940
case <-time.After(time.Second):
5041
t.Fatal("timeout")
5142
}
52-
}
5343

54-
func Test_DeadLock2(t *testing.T) {
55-
testDone := make(chan int)
44+
}
5645

57-
var (
58-
mutex1 Mutex
59-
mutex2 Mutex
46+
func Test_DeadLock1(t *testing.T) {
47+
deadlockTest(t, func() {
48+
var mutex1 Mutex
6049

61-
wait1 WaitGroup
62-
)
50+
mutex1.Lock()
51+
mutex1.Lock()
52+
})
53+
}
6354

64-
wait1.Add(1)
65-
go func() {
66-
defer func() {
67-
err := recover()
68-
testRecover(err, testDone)
69-
}()
55+
func Test_DeadLock2(t *testing.T) {
56+
deadlockTest(t, func() {
57+
var (
58+
mutex1 Mutex
59+
mutex2 Mutex
60+
)
7061

7162
mutex1.Lock()
63+
64+
var wait1 WaitGroup
65+
wait1.Add(1)
66+
go func() {
67+
mutex2.Lock()
68+
wait1.Done()
69+
mutex1.Lock()
70+
}()
7271
wait1.Wait()
73-
mutex2.Lock()
74-
}()
7572

76-
go func() {
7773
mutex2.Lock()
78-
wait1.Done()
79-
mutex1.Lock()
80-
}()
81-
82-
select {
83-
case <-testDone:
84-
case <-time.After(time.Second):
85-
t.Fatal("timeout")
86-
}
74+
})
8775
}
8876

8977
func Test_DeadLock3(t *testing.T) {
90-
testDone := make(chan int)
91-
92-
var (
93-
mutex1 Mutex
94-
mutex2 Mutex
95-
mutex3 Mutex
78+
deadlockTest(t, func() {
79+
var (
80+
mutex1 Mutex
81+
mutex2 Mutex
82+
mutex3 Mutex
83+
)
9684

97-
wait1 WaitGroup
98-
wait2 WaitGroup
99-
)
100-
101-
wait1.Add(1)
102-
wait2.Add(1)
85+
mutex1.Lock()
10386

104-
go func() {
105-
defer func() {
106-
err := recover()
107-
testRecover(err, testDone)
87+
var wait1 WaitGroup
88+
wait1.Add(1)
89+
go func() {
90+
mutex2.Lock()
91+
92+
var wait2 WaitGroup
93+
wait2.Add(1)
94+
go func() {
95+
mutex3.Lock()
96+
wait2.Done()
97+
mutex2.Lock()
98+
}()
99+
wait2.Wait()
100+
101+
wait1.Done()
102+
mutex1.Lock()
108103
}()
109-
110-
mutex1.Lock()
111104
wait1.Wait()
112-
mutex2.Lock()
113-
}()
114105

115-
go func() {
116-
mutex2.Lock()
117-
wait2.Wait()
118-
wait1.Done()
119-
mutex3.Lock()
120-
}()
121-
122-
go func() {
123106
mutex3.Lock()
124-
wait2.Done()
125-
mutex1.Lock()
126-
}()
127-
128-
select {
129-
case <-testDone:
130-
case <-time.After(time.Second):
131-
t.Fatal("timeout")
132-
}
107+
})
133108
}

0 commit comments

Comments
 (0)