fix: discovery AyncFilter deadlock on shutdown (#3347)

This commit is contained in:
zzzckck 2025-09-10 15:42:12 +08:00
parent 5cc443609f
commit bfa5f14826

View file

@ -178,7 +178,7 @@ type AsyncFilterFunc func(context.Context, *Node) *Node
func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator { func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
f := &asyncFilterIter{ f := &asyncFilterIter{
it: ensureSourceIter(it), it: ensureSourceIter(it),
slots: make(chan struct{}, workers+1), slots: make(chan struct{}, workers+1), // extra 1 slot to make sure all the goroutines can be completed
passed: make(chan iteratorItem), passed: make(chan iteratorItem),
} }
for range cap(f.slots) { for range cap(f.slots) {
@ -193,6 +193,9 @@ func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
return return
case <-f.slots: case <-f.slots:
} }
defer func() {
f.slots <- struct{}{} // the iterator has ended
}()
// read from the iterator and start checking nodes in parallel // read from the iterator and start checking nodes in parallel
// when a node is checked, it will be sent to the passed channel // when a node is checked, it will be sent to the passed channel
// and the slot will be released // and the slot will be released
@ -201,7 +204,11 @@ func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
nodeSource := f.it.NodeSource() nodeSource := f.it.NodeSource()
// check the node async, in a separate goroutine // check the node async, in a separate goroutine
<-f.slots select {
case <-ctx.Done():
return
case <-f.slots:
}
go func() { go func() {
if nn := check(ctx, node); nn != nil { if nn := check(ctx, node); nn != nil {
item := iteratorItem{nn, nodeSource} item := iteratorItem{nn, nodeSource}
@ -213,8 +220,6 @@ func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
f.slots <- struct{}{} f.slots <- struct{}{}
}() }()
} }
// the iterator has ended
f.slots <- struct{}{}
}() }()
return f return f