Skip to content

Commit

Permalink
Make sure removed/resetted user valeus get garbage collected
Browse files Browse the repository at this point in the history
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 #1812
  • Loading branch information
erikdubbelboer committed Jul 28, 2024
1 parent a7d488a commit 0324e8d
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 0 deletions.
3 changes: 3 additions & 0 deletions userdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
Expand All @@ -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
Expand Down
31 changes: 31 additions & 0 deletions userdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package fasthttp
import (
"fmt"
"reflect"
"runtime"
"testing"
"time"
)

func TestUserData(t *testing.T) {
Expand Down Expand Up @@ -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)
}

0 comments on commit 0324e8d

Please sign in to comment.