From 0324e8d2a2831fa30478ced9202499d6cff14979 Mon Sep 17 00:00:00 2001 From: Erik Dubbelboer Date: Sun, 28 Jul 2024 11:06:17 +0200 Subject: [PATCH] Make sure removed/resetted user valeus get garbage collected Since our userData slice isn't shrunk when we delete values, it can still keep pointing to things that have been removed. Set these pointers to nil so that the key and value can be garbage collected. Fixes https://github.com/valyala/fasthttp/issues/1812 --- userdata.go | 3 +++ userdata_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/userdata.go b/userdata.go index 38cca86..20366b6 100644 --- a/userdata.go +++ b/userdata.go @@ -77,6 +77,8 @@ func (d *userData) Reset() { if vc, ok := v.(io.Closer); ok { vc.Close() } + (*d)[i].value = nil + (*d)[i].key = nil } *d = (*d)[:0] } @@ -92,6 +94,7 @@ func (d *userData) Remove(key any) { if kv.key == key { n-- args[i], args[n] = args[n], args[i] + args[n].key = nil args[n].value = nil args = args[:n] *d = args diff --git a/userdata_test.go b/userdata_test.go index fd6a924..1e19044 100644 --- a/userdata_test.go +++ b/userdata_test.go @@ -3,7 +3,9 @@ package fasthttp import ( "fmt" "reflect" + "runtime" "testing" + "time" ) func TestUserData(t *testing.T) { @@ -118,3 +120,32 @@ func TestUserDataSetAndRemove(t *testing.T) { testUserDataGet(t, &u, []byte(shortKey), "") testUserDataGet(t, &u, []byte(longKey), "") } + +func TestUserData_GC(t *testing.T) { + t.Parallel() + + var u userData + key := "foo" + final := make(chan struct{}) + + func() { + val := &RequestHeader{} + runtime.SetFinalizer(val, func(v *RequestHeader) { + close(final) + }) + + u.Set(key, val) + }() + + u.Reset() + runtime.GC() + + select { + case <-final: + case <-time.After(time.Second): + t.Fatalf("value is garbage collected") + } + + // Keep u alive, otherwise val will always get garbage collected. + u.Set("bar", 1) +}