From a0a32974813f86fa308fb38fd68d82a39cdcb159 Mon Sep 17 00:00:00 2001 From: Sahil Sojitra Date: Tue, 28 Apr 2026 14:42:50 +0530 Subject: [PATCH 1/2] fix: preserve next element when removing expired matchers --- p2p/discover/common.go | 15 +++++++++++++++ p2p/discover/v4_udp.go | 11 ++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/p2p/discover/common.go b/p2p/discover/common.go index 033b3d9517e..ada72317a46 100644 --- a/p2p/discover/common.go +++ b/p2p/discover/common.go @@ -20,9 +20,11 @@ package discover import ( + "container/list" "crypto/ecdsa" crand "crypto/rand" "encoding/binary" + "iter" "math/rand" "net" "net/netip" @@ -146,3 +148,16 @@ func (r *reseedingRandom) Shuffle(n int, swap func(i, j int)) { defer r.mu.Unlock() r.cur.Shuffle(n, swap) } + +// iterList iterates over the elements of the given list. +func iterList[T any](l *list.List) iter.Seq2[T, *list.Element] { + return func(yield func(T, *list.Element) bool) { + for el := l.Front(); el != nil; { + next := el.Next() + if !yield(el.Value.(T), el) { + return + } + el = next + } + } +} diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index c9fe6f624a5..588da24cb92 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -452,9 +452,8 @@ func (t *UDPv4) loop() { } // Start the timer so it fires when the next pending reply has expired. now := time.Now() - for el := plist.Front(); el != nil; el = el.Next() { - nextTimeout = el.Value.(*replyMatcher) - if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout { + for p, el := range iterList[*replyMatcher](plist) { + if dist := p.deadline.Sub(now); dist < 2*respTimeout { timeout.Reset(dist) return } @@ -484,8 +483,7 @@ func (t *UDPv4) loop() { case r := <-t.gotreply: var matched bool // whether any replyMatcher considered the reply acceptable. - for el := plist.Front(); el != nil; el = el.Next() { - p := el.Value.(*replyMatcher) + for p, el := range iterList[*replyMatcher](plist) { if p.from == r.from && p.ptype == r.data.Kind() && p.ip == r.ip { ok, requestDone := p.callback(r.data) matched = matched || ok @@ -505,8 +503,7 @@ func (t *UDPv4) loop() { nextTimeout = nil // Notify and remove callbacks whose deadline is in the past. - for el := plist.Front(); el != nil; el = el.Next() { - p := el.Value.(*replyMatcher) + for p, el := range iterList[*replyMatcher](plist) { if now.After(p.deadline) || now.Equal(p.deadline) { p.errc <- errTimeout plist.Remove(el) From 04e045f8d38070e36561e0f9371ca515937b14dc Mon Sep 17 00:00:00 2001 From: Sahil Sojitra Date: Thu, 30 Apr 2026 09:35:53 +0530 Subject: [PATCH 2/2] fix: restore nextTimeout tracking in resetTimeout --- p2p/discover/v4_udp.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index 588da24cb92..daf1eb261f0 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -453,6 +453,7 @@ func (t *UDPv4) loop() { // Start the timer so it fires when the next pending reply has expired. now := time.Now() for p, el := range iterList[*replyMatcher](plist) { + nextTimeout = p if dist := p.deadline.Sub(now); dist < 2*respTimeout { timeout.Reset(dist) return @@ -460,7 +461,7 @@ func (t *UDPv4) loop() { // Remove pending replies whose deadline is too far in the // future. These can occur if the system clock jumped // backwards after the deadline was assigned. - nextTimeout.errc <- errClockWarp + p.errc <- errClockWarp plist.Remove(el) } nextTimeout = nil