mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +00:00
Merge pull request #1321 from abursavich/master
pkg/watch: fix potential deadlock
This commit is contained in:
commit
9c25792dca
@ -57,6 +57,7 @@ func (m *Mux) Watch() Interface {
|
|||||||
m.nextWatcher++
|
m.nextWatcher++
|
||||||
w := &muxWatcher{
|
w := &muxWatcher{
|
||||||
result: make(chan Event),
|
result: make(chan Event),
|
||||||
|
stopped: make(chan struct{}),
|
||||||
id: id,
|
id: id,
|
||||||
m: m,
|
m: m,
|
||||||
}
|
}
|
||||||
@ -114,18 +115,26 @@ func (m *Mux) loop() {
|
|||||||
m.closeAll()
|
m.closeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testHookMuxDistribute = func() {}
|
||||||
|
|
||||||
// distribute sends event to all watchers. Blocking.
|
// distribute sends event to all watchers. Blocking.
|
||||||
func (m *Mux) distribute(event Event) {
|
func (m *Mux) distribute(event Event) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
testHookMuxDistribute()
|
||||||
for _, w := range m.watchers {
|
for _, w := range m.watchers {
|
||||||
w.result <- event
|
select {
|
||||||
|
case w.result <- event:
|
||||||
|
case <-w.stopped:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// muxWatcher handles a single watcher of a mux
|
// muxWatcher handles a single watcher of a mux
|
||||||
type muxWatcher struct {
|
type muxWatcher struct {
|
||||||
result chan Event
|
result chan Event
|
||||||
|
stopped chan struct{}
|
||||||
|
stop sync.Once
|
||||||
id int64
|
id int64
|
||||||
m *Mux
|
m *Mux
|
||||||
}
|
}
|
||||||
@ -137,5 +146,8 @@ func (mw *muxWatcher) ResultChan() <-chan Event {
|
|||||||
|
|
||||||
// Stop stops watching and removes mw from its list.
|
// Stop stops watching and removes mw from its list.
|
||||||
func (mw *muxWatcher) Stop() {
|
func (mw *muxWatcher) Stop() {
|
||||||
|
mw.stop.Do(func() {
|
||||||
|
close(mw.stopped)
|
||||||
mw.m.stopWatching(mw.id)
|
mw.m.stopWatching(mw.id)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type myType struct {
|
type myType struct {
|
||||||
@ -91,3 +92,24 @@ func TestMuxWatcherClose(t *testing.T) {
|
|||||||
w.Stop()
|
w.Stop()
|
||||||
w2.Stop()
|
w2.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMuxWatcherStopDeadlock(t *testing.T) {
|
||||||
|
defer func(fn func()) { testHookMuxDistribute = fn }(testHookMuxDistribute)
|
||||||
|
sig, done := make(chan bool), make(chan bool)
|
||||||
|
testHookMuxDistribute = func() { sig <- true }
|
||||||
|
m := NewMux(0)
|
||||||
|
go func(w Interface) {
|
||||||
|
// Imagine this goroutine was receiving from w.ResultChan()
|
||||||
|
// until it received some signal and stopped watching.
|
||||||
|
<-sig
|
||||||
|
w.Stop()
|
||||||
|
close(done)
|
||||||
|
}(m.Watch())
|
||||||
|
m.Action(Added, &myType{})
|
||||||
|
select {
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Error("timeout: deadlocked")
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
m.Shutdown()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user