Skip to content

Commit 7da26f4

Browse files
committed
Test for goroutine leak GC.
1 parent 710b419 commit 7da26f4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+8071
-140
lines changed

src/runtime/gc_test.go

Lines changed: 393 additions & 112 deletions
Large diffs are not rendered by default.
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Project: cockroach
3+
* Issue or PR : https://github.com/cockroachdb/cockroach/pull/10214
4+
* Buggy version: 7207111aa3a43df0552509365fdec741a53f873f
5+
* fix commit-id: 27e863d90ab0660494778f1c35966cc5ddc38e32
6+
* Flaky: 3/100
7+
* Description: This deadlock is caused by different order when acquiring
8+
* coalescedMu.Lock() and raftMu.Lock(). The fix is to refactor sendQueuedHeartbeats()
9+
* so that cockroachdb can unlock coalescedMu before locking raftMu.
10+
*/
11+
package main
12+
13+
import (
14+
"runtime"
15+
"sync"
16+
"time"
17+
"unsafe"
18+
)
19+
20+
func init() {
21+
register("Cockroach10214", Cockroach10214)
22+
}
23+
24+
type Store_cockroach10214 struct {
25+
coalescedMu struct {
26+
sync.Mutex
27+
heartbeatResponses []int
28+
}
29+
mu struct {
30+
replicas map[int]*Replica_cockroach10214
31+
}
32+
}
33+
34+
func (s *Store_cockroach10214) sendQueuedHeartbeats() {
35+
s.coalescedMu.Lock() // LockA acquire
36+
runtime.Gosched()
37+
defer s.coalescedMu.Unlock()
38+
for i := 0; i < len(s.coalescedMu.heartbeatResponses); i++ {
39+
s.sendQueuedHeartbeatsToNode() // LockB
40+
}
41+
// LockA release
42+
}
43+
44+
func (s *Store_cockroach10214) sendQueuedHeartbeatsToNode() {
45+
for i := 0; i < len(s.mu.replicas); i++ {
46+
r := s.mu.replicas[i]
47+
r.reportUnreachable() // LockB
48+
}
49+
}
50+
51+
type Replica_cockroach10214 struct {
52+
raftMu sync.Mutex
53+
mu sync.Mutex
54+
store *Store_cockroach10214
55+
}
56+
57+
func (r *Replica_cockroach10214) reportUnreachable() {
58+
r.raftMu.Lock() // LockB acquire
59+
runtime.Gosched()
60+
//+time.Sleep(time.Nanosecond)
61+
defer r.raftMu.Unlock()
62+
// LockB release
63+
}
64+
65+
func (r *Replica_cockroach10214) tick() {
66+
r.raftMu.Lock() // LockB acquire
67+
runtime.Gosched()
68+
defer r.raftMu.Unlock()
69+
r.tickRaftMuLocked()
70+
// LockB release
71+
}
72+
73+
func (r *Replica_cockroach10214) tickRaftMuLocked() {
74+
r.mu.Lock()
75+
defer r.mu.Unlock()
76+
if r.maybeQuiesceLocked() {
77+
return
78+
}
79+
}
80+
func (r *Replica_cockroach10214) maybeQuiesceLocked() bool {
81+
for i := 0; i < 2; i++ {
82+
if !r.maybeCoalesceHeartbeat() {
83+
return true
84+
}
85+
}
86+
return false
87+
}
88+
func (r *Replica_cockroach10214) maybeCoalesceHeartbeat() bool {
89+
msgtype := uintptr(unsafe.Pointer(r)) % 3
90+
switch msgtype {
91+
case 0, 1, 2:
92+
r.store.coalescedMu.Lock() // LockA acquire
93+
default:
94+
return false
95+
}
96+
r.store.coalescedMu.Unlock() // LockA release
97+
return true
98+
}
99+
100+
func Cockroach10214() {
101+
defer func() {
102+
time.Sleep(100 * time.Millisecond)
103+
runtime.GC()
104+
}()
105+
for i := 0; i < 1000; i++ {
106+
go func() {
107+
store := &Store_cockroach10214{}
108+
responses := &store.coalescedMu.heartbeatResponses
109+
*responses = append(*responses, 1, 2)
110+
store.mu.replicas = make(map[int]*Replica_cockroach10214)
111+
112+
rp1 := &Replica_cockroach10214{
113+
store: store,
114+
}
115+
rp2 := &Replica_cockroach10214{
116+
store: store,
117+
}
118+
store.mu.replicas[0] = rp1
119+
store.mu.replicas[1] = rp2
120+
121+
go func() {
122+
// deadlocks: x > 0
123+
store.sendQueuedHeartbeats()
124+
}()
125+
126+
go func() {
127+
// deadlocks: x > 0
128+
rp1.tick()
129+
}()
130+
131+
}()
132+
}
133+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package main
2+
3+
import (
4+
"runtime"
5+
"sync"
6+
"sync/atomic"
7+
"time"
8+
)
9+
10+
func init() {
11+
register("Cockroach1055", Cockroach1055)
12+
}
13+
14+
type Stopper_cockroach1055 struct {
15+
stopper chan struct{}
16+
stop sync.WaitGroup
17+
mu sync.Mutex
18+
draining int32
19+
drain sync.WaitGroup
20+
}
21+
22+
func (s *Stopper_cockroach1055) AddWorker() {
23+
s.stop.Add(1)
24+
}
25+
26+
func (s *Stopper_cockroach1055) ShouldStop() <-chan struct{} {
27+
if s == nil {
28+
return nil
29+
}
30+
return s.stopper
31+
}
32+
33+
func (s *Stopper_cockroach1055) SetStopped() {
34+
if s != nil {
35+
s.stop.Done()
36+
}
37+
}
38+
39+
func (s *Stopper_cockroach1055) Quiesce() {
40+
s.mu.Lock()
41+
defer s.mu.Unlock()
42+
s.draining = 1
43+
s.drain.Wait()
44+
s.draining = 0
45+
}
46+
47+
func (s *Stopper_cockroach1055) Stop() {
48+
s.mu.Lock() // L1
49+
defer s.mu.Unlock()
50+
atomic.StoreInt32(&s.draining, 1)
51+
s.drain.Wait()
52+
close(s.stopper)
53+
s.stop.Wait()
54+
}
55+
56+
func (s *Stopper_cockroach1055) StartTask() bool {
57+
if atomic.LoadInt32(&s.draining) == 0 {
58+
s.mu.Lock()
59+
defer s.mu.Unlock()
60+
s.drain.Add(1)
61+
return true
62+
}
63+
return false
64+
}
65+
66+
func NewStopper_cockroach1055() *Stopper_cockroach1055 {
67+
return &Stopper_cockroach1055{
68+
stopper: make(chan struct{}),
69+
}
70+
}
71+
72+
func Cockroach1055() {
73+
defer func() {
74+
time.Sleep(1 * time.Second)
75+
runtime.GC()
76+
}()
77+
78+
for i := 0; i <= 1000; i++ {
79+
go func() { // G1
80+
// deadlocks: x > 0
81+
var stoppers []*Stopper_cockroach1055
82+
for i := 0; i < 2; i++ {
83+
stoppers = append(stoppers, NewStopper_cockroach1055())
84+
}
85+
86+
for i := range stoppers {
87+
s := stoppers[i]
88+
s.AddWorker()
89+
go func() { // G2
90+
// deadlocks: x > 0
91+
s.StartTask()
92+
<-s.ShouldStop()
93+
s.SetStopped()
94+
}()
95+
}
96+
97+
done := make(chan struct{})
98+
go func() { // G3
99+
// deadlocks: x > 0
100+
for _, s := range stoppers {
101+
s.Quiesce()
102+
}
103+
for _, s := range stoppers {
104+
s.Stop()
105+
}
106+
close(done)
107+
}()
108+
109+
<-done
110+
}()
111+
}
112+
}
113+
114+
// Example deadlock trace:
115+
//
116+
// G1 G2.0 G2.1 G2.2 G3
117+
// ---------------------------------------------------------------------------------------------------------------------
118+
// s[0].stop.Add(1) [1]
119+
// go func() [G2.0]
120+
// s[1].stop.Add(1) [1] .
121+
// go func() [G2.1] .
122+
// s[2].stop.Add(1) [1] . .
123+
// go func() [G2.2] . .
124+
// go func() [G3] . . .
125+
// <-done . . . .
126+
// . s[0].StartTask() . . .
127+
// . s[0].draining == 0 . . .
128+
// . . s[1].StartTask() . .
129+
// . . s[1].draining == 0 . .
130+
// . . . s[2].StartTask() .
131+
// . . . s[2].draining == 0 .
132+
// . . . . s[0].Quiesce()
133+
// . . . . s[0].mu.Lock() [L1[0]]
134+
// . s[0].mu.Lock() [L1[0]] . . .
135+
// . s[0].drain.Add(1) [1] . . .
136+
// . s[0].mu.Unlock() [L1[0]] . . .
137+
// . <-s[0].ShouldStop() . . .
138+
// . . . . s[0].draining = 1
139+
// . . . . s[0].drain.Wait()
140+
// . . s[0].mu.Lock() [L1[1]] . .
141+
// . . s[1].drain.Add(1) [1] . .
142+
// . . s[1].mu.Unlock() [L1[1]] . .
143+
// . . <-s[1].ShouldStop() . .
144+
// . . . s[2].mu.Lock() [L1[2]] .
145+
// . . . s[2].drain.Add() [1] .
146+
// . . . s[2].mu.Unlock() [L1[2]] .
147+
// . . . <-s[2].ShouldStop() .
148+
// ----------------------------------------------------G1, G2.[0..2], G3 leak------------------------------------------------

0 commit comments

Comments
 (0)