Skip to content

Commit f765ef2

Browse files
authored
Merge pull request #47 from jolestar/fix_dead_lock
Fix dead lock
2 parents d5a2e34 + fbe896a commit f765ef2

File tree

4 files changed

+58
-9
lines changed

4 files changed

+58
-9
lines changed

collections/queue_test.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ func (suit *LinkedBlockDequeTestSuite) TestInterrupt() {
252252
for i := 0; i < 2; i++ {
253253
_, e := suit.deque.TakeFirst(ctx)
254254
_, ok := e.(*InterruptedErr)
255-
suit.True(ok, "expect InterruptedErr bug get %v", reflect.TypeOf(e))
255+
suit.True(ok, "expect InterruptedErr but get %v", reflect.TypeOf(e))
256256
suit.NotNil(e.Error())
257257
}
258258
wait.Wait()
@@ -502,3 +502,21 @@ func (suit *LinkedBlockDequeTestSuite) TestHasTakeWaiters() {
502502
suit.Equal(1, val)
503503
suit.False(suit.deque.HasTakeWaiters())
504504
}
505+
506+
// https://github.com/jolestar/go-commons-pool/issues/44
507+
func (suit *LinkedBlockDequeTestSuite) TestDeadLock() {
508+
ctx := context.Background()
509+
suit.deque = NewDeque(1)
510+
suit.deque.PutFirst(ctx, 1)
511+
count := 1000000
512+
testWG := sync.WaitGroup{}
513+
testWG.Add(count)
514+
for i := 0; i < count; i++ {
515+
o := suit.NoErrorWithResult(suit.deque.PollFirstWithContext(ctx))
516+
go func() {
517+
suit.deque.PutFirst(ctx, o)
518+
testWG.Done()
519+
}()
520+
}
521+
testWG.Wait()
522+
}

concurrent/cond.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ type TimeoutCond struct {
1313
hasWaiters uint64
1414
L sync.Locker
1515
signal chan int
16+
condL sync.RWMutex
1617
}
1718

1819
// NewTimeoutCond return a new TimeoutCond
1920
func NewTimeoutCond(l sync.Locker) *TimeoutCond {
20-
cond := TimeoutCond{L: l, signal: make(chan int, 0)}
21+
cond := TimeoutCond{L: l, signal: make(chan int, 1), condL: sync.RWMutex{}}
2122
return &cond
2223
}
2324

@@ -45,8 +46,11 @@ func (cond *TimeoutCond) HasWaiters() bool {
4546
// Wait waits for a signal, or for the context do be done. Returns true if signaled.
4647
func (cond *TimeoutCond) Wait(ctx context.Context) bool {
4748
cond.addWaiter()
49+
50+
cond.condL.RLock()
4851
//copy signal in lock, avoid data race with Interrupt
4952
ch := cond.signal
53+
cond.condL.RUnlock()
5054
//wait should unlock mutex, if not will cause deadlock
5155
cond.L.Unlock()
5256
defer cond.removeWaiter()
@@ -62,16 +66,18 @@ func (cond *TimeoutCond) Wait(ctx context.Context) bool {
6266

6367
// Signal wakes one goroutine waiting on c, if there is any.
6468
func (cond *TimeoutCond) Signal() {
69+
cond.condL.RLock()
6570
select {
6671
case cond.signal <- 1:
6772
default:
6873
}
74+
cond.condL.RUnlock()
6975
}
7076

7177
// Interrupt goroutine wait on this TimeoutCond
7278
func (cond *TimeoutCond) Interrupt() {
73-
cond.L.Lock()
74-
defer cond.L.Unlock()
79+
cond.condL.Lock()
80+
defer cond.condL.Unlock()
7581
close(cond.signal)
7682
cond.signal = make(chan int, 0)
7783
}

concurrent/cond_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ func TestTimeoutCondWaitTimeoutNotify(t *testing.T) {
9090
wait := sync.WaitGroup{}
9191
wait.Add(2)
9292
ch := make(chan time.Duration, 1)
93-
timeout := 2 * time.Second
93+
timeout := 5 * time.Second
9494
go func() {
9595
begin := time.Now()
96-
obj.lockAndWaitWithTimeout(time.Duration(timeout) * time.Millisecond)
96+
obj.lockAndWaitWithTimeout(timeout * time.Millisecond)
9797
elapsed := time.Since(begin)
9898
ch <- elapsed
9999
wait.Done()
@@ -196,7 +196,7 @@ func TestInterrupted(t *testing.T) {
196196
wait.Wait()
197197
for i := 0; i < count; i++ {
198198
b := <-ch
199-
assert.True(t, b, "expect %v interrupted bug get false", i)
199+
assert.True(t, b, "expect %v interrupted but get false", i)
200200
}
201201
}
202202

@@ -222,7 +222,7 @@ func TestInterruptedWithTimeout(t *testing.T) {
222222
wait.Wait()
223223
for i := 0; i < count; i++ {
224224
b := <-ch
225-
assert.True(t, b, "expect %v interrupted bug get false", i)
225+
assert.True(t, b, "expect %v interrupted but get false", i)
226226
}
227227
}
228228

pool_test.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,6 @@ func (suit *PoolTestSuite) TestEvictionSoftMinIdle() {
13171317
suit.Equal(0, suit.pool.GetNumIdle(), "Idle count different than expected.")
13181318
}
13191319

1320-
13211320
func (suit *PoolTestSuite) TestEvictionNegativeIdleTime() {
13221321
suit.pool.Config.MaxIdle = 5
13231322
suit.pool.Config.MaxTotal = 5
@@ -2070,6 +2069,32 @@ func (suit *PoolTestSuite) TestValueFactory() {
20702069
})
20712070
}
20722071

2072+
// https://github.com/jolestar/go-commons-pool/issues/44
2073+
func (suit *PoolTestSuite) TestDeadLock() {
2074+
ctx := context.Background()
2075+
suit.pool.Config.MinIdle = 1
2076+
suit.pool.Config.MaxIdle = 1
2077+
suit.pool.Config.MaxTotal = 1
2078+
count := 1000000
2079+
testWG := sync.WaitGroup{}
2080+
testWG.Add(count)
2081+
2082+
for i := 0; i < count; i++ {
2083+
obj, err := suit.pool.BorrowObject(ctx)
2084+
if err != nil {
2085+
panic(err)
2086+
}
2087+
go func(obj interface{}) {
2088+
err = suit.pool.ReturnObject(ctx, obj)
2089+
if err != nil {
2090+
panic(err)
2091+
}
2092+
testWG.Done()
2093+
}(obj)
2094+
}
2095+
testWG.Wait()
2096+
}
2097+
20732098
var perf bool
20742099

20752100
func init() {

0 commit comments

Comments
 (0)