From 3b8082a10c4e55224e95bc29837ebec05975d874 Mon Sep 17 00:00:00 2001 From: GeunhwaJeong Date: Tue, 7 Jul 2026 13:37:16 +0900 Subject: [PATCH] core/state: prevent build goroutine leak when SizeTracker is stopped SizeTracker.build delivers its result on an unbuffered channel. If the tracker is stopped while the initial measurement is running, init returns via the abort arm without receiving, and the build goroutine blocks forever on the send, regardless of whether the measurement failed or completed. Make both send sites select on the abort channel so the goroutine always terminates. --- core/state/state_sizer.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/state/state_sizer.go b/core/state/state_sizer.go index 3293d7e950..77eb604ad2 100644 --- a/core/state/state_sizer.go +++ b/core/state/state_sizer.go @@ -538,7 +538,12 @@ func (t *SizeTracker) build(root common.Hash, blockNumber uint64, done chan buil // Wait for all goroutines to complete if err := group.Wait(); err != nil { - done <- buildResult{err: err} + select { + case done <- buildResult{err: err}: + case <-t.abort: + // The tracker was closed and the receiver in init has + // already exited, the result is no longer consumed. + } } else { stat := SizeStats{ StateRoot: root, @@ -554,11 +559,16 @@ func (t *SizeTracker) build(root common.Hash, blockNumber uint64, done chan buil ContractCodes: codes, ContractCodeBytes: codeBytes, } - done <- buildResult{ + select { + case done <- buildResult{ root: root, blockNumber: blockNumber, stat: stat, elapsed: time.Since(start), + }: + case <-t.abort: + // The tracker was closed and the receiver in init has + // already exited, the result is no longer consumed. } } }