From 85e81382ad79d34f7b9a2874c3acf8d792502c45 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 7 May 2026 12:39:02 +0200 Subject: [PATCH] p2p/discover: decouple nodeFeed from Table mutex in waitForNodes --- p2p/discover/table.go | 51 +++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/p2p/discover/table.go b/p2p/discover/table.go index 721cd7b589..e9c7a3b10b 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -753,6 +753,36 @@ func (tab *Table) deleteNode(n *enode.Node) { // waitForNodes blocks until the table contains at least n nodes. func (tab *Table) waitForNodes(ctx context.Context, n int) error { + // Set up a notification channel that gets unblocked when there was any activity on + // the table. Ultimately this reads from the table's nodeFeed, but can't use the feed + // directly on the same goroutine that takes Table.mutex, it would deadlock. + var notify chan struct{} + var notifyErr error + initsub := func() event.Subscription { + notify = make(chan struct{}, 1) + newnode := make(chan *enode.Node, 1) + sub := tab.nodeFeed.Subscribe(newnode) + go func() { + defer close(notify) + for { + select { + case <-newnode: + select { + case notify <- struct{}{}: + default: + } + case <-ctx.Done(): + notifyErr = ctx.Err() + return + case <-tab.closeReq: + notifyErr = errClosed + return + } + } + }() + return sub + } + getlength := func() (count int) { for _, b := range &tab.buckets { count += len(b.entries) @@ -760,28 +790,25 @@ func (tab *Table) waitForNodes(ctx context.Context, n int) error { return count } - var ch chan *enode.Node for { tab.mutex.Lock() if getlength() >= n { tab.mutex.Unlock() return nil } - if ch == nil { - // Init subscription. - ch = make(chan *enode.Node) - sub := tab.nodeFeed.Subscribe(ch) + if notify == nil { + // Lazily init the subscription. + sub := initsub() defer sub.Unsubscribe() } tab.mutex.Unlock() - // Wait for a node add event. - select { - case <-ch: - case <-ctx.Done(): - return ctx.Err() - case <-tab.closeReq: - return errClosed + // Wait for table event. + _, ok := <-notify + if !ok { + break } } + return notifyErr + }