fix: filters.FilterAPI goroutine leak (#254)

## Why this should be merged

This allows closing the `timeoutLoop` goroutine. Closing goroutines is
the best-practice. But, more importantly, this causes the
`filters.Backend` to never be GCed. Which is causing GC tests to fail in
SAE.

## How this works

I tried to make this as minimal of a diff as possible.

## How this was tested

With this change, the SAE tests are passing:
https://github.com/ava-labs/strevm/pull/64
This commit is contained in:
Stephen Buttolph 2026-01-03 16:54:33 -05:00 committed by GitHub
parent 505c2fbe72
commit d8ff026f89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 90 additions and 1 deletions

View file

@ -64,6 +64,8 @@ type FilterAPI struct {
filtersMu sync.Mutex
filters map[rpc.ID]*filter
timeout time.Duration
quit chan struct{}
}
// NewFilterAPI returns a new FilterAPI instance.
@ -73,6 +75,7 @@ func NewFilterAPI(system *FilterSystem, lightMode bool) *FilterAPI {
events: NewEventSystem(system, lightMode),
filters: make(map[rpc.ID]*filter),
timeout: system.cfg.Timeout,
quit: make(chan struct{}),
}
go api.timeoutLoop(system.cfg.Timeout)
@ -86,7 +89,11 @@ func (api *FilterAPI) timeoutLoop(timeout time.Duration) {
ticker := time.NewTicker(timeout)
defer ticker.Stop()
for {
<-ticker.C
select {
case <-ticker.C:
case <-api.quit:
return
}
api.filtersMu.Lock()
for id, f := range api.filters {
select {

22
eth/filters/api.libevm.go Normal file
View file

@ -0,0 +1,22 @@
// Copyright 2026 the libevm authors.
//
// The libevm additions to go-ethereum are free software: you can redistribute
// them and/or modify them under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// The libevm additions are distributed in the hope that they will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see
// <http://www.gnu.org/licenses/>.
package filters
// Close releases resources held by the API.
func (api *FilterAPI) Close() {
close(api.quit)
}

View file

@ -0,0 +1,57 @@
// Copyright 2026 the libevm authors.
//
// The libevm additions to go-ethereum are free software: you can redistribute
// them and/or modify them under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// The libevm additions are distributed in the hope that they will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see
// <http://www.gnu.org/licenses/>.
package filters
import (
"testing"
"go.uber.org/goleak"
"github.com/ava-labs/libevm/core"
"github.com/ava-labs/libevm/core/rawdb"
"github.com/ava-labs/libevm/event"
)
// A closeableTestBackend tracks all subscriptions that it produces, allowing
// them to be cleaned up to avoid the leak of [EventSystem.eventLoop].
type closeableTestBackend struct {
testBackend
subs event.SubscriptionScope
}
func (b *closeableTestBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
sub := b.testBackend.SubscribeNewTxsEvent(ch)
return b.subs.Track(sub)
}
func (b *closeableTestBackend) Close() {
b.subs.Close()
}
func TestClose(t *testing.T) {
defer goleak.VerifyNone(t, goleak.IgnoreCurrent())
backend := &closeableTestBackend{
testBackend: testBackend{
db: rawdb.NewMemoryDatabase(),
},
}
defer backend.Close()
sys := NewFilterSystem(backend, Config{})
api := NewFilterAPI(sys, false)
api.Close()
}

1
go.mod
View file

@ -64,6 +64,7 @@ require (
github.com/tyler-smith/go-bip39 v1.1.0
github.com/urfave/cli/v2 v2.25.7
go.uber.org/automaxprocs v1.5.2
go.uber.org/goleak v1.3.0
golang.org/x/crypto v0.43.0
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/mod v0.29.0

2
go.sum
View file

@ -622,6 +622,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=