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
This commit is contained in:
Erik Dubbelboer
2024-07-28 11:06:17 +02:00
parent a7d488a91c
commit 0324e8d2a2
2 changed files with 34 additions and 0 deletions
+3
View File
@@ -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
+31
View File
@@ -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)
}