Skip to content

Commit 0324e8d

Browse files
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 #1812
1 parent a7d488a commit 0324e8d

File tree

2 files changed

+34
-0
lines changed

2 files changed

+34
-0
lines changed

userdata.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ func (d *userData) Reset() {
7777
if vc, ok := v.(io.Closer); ok {
7878
vc.Close()
7979
}
80+
(*d)[i].value = nil
81+
(*d)[i].key = nil
8082
}
8183
*d = (*d)[:0]
8284
}
@@ -92,6 +94,7 @@ func (d *userData) Remove(key any) {
9294
if kv.key == key {
9395
n--
9496
args[i], args[n] = args[n], args[i]
97+
args[n].key = nil
9598
args[n].value = nil
9699
args = args[:n]
97100
*d = args

userdata_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package fasthttp
33
import (
44
"fmt"
55
"reflect"
6+
"runtime"
67
"testing"
8+
"time"
79
)
810

911
func TestUserData(t *testing.T) {
@@ -118,3 +120,32 @@ func TestUserDataSetAndRemove(t *testing.T) {
118120
testUserDataGet(t, &u, []byte(shortKey), "")
119121
testUserDataGet(t, &u, []byte(longKey), "")
120122
}
123+
124+
func TestUserData_GC(t *testing.T) {
125+
t.Parallel()
126+
127+
var u userData
128+
key := "foo"
129+
final := make(chan struct{})
130+
131+
func() {
132+
val := &RequestHeader{}
133+
runtime.SetFinalizer(val, func(v *RequestHeader) {
134+
close(final)
135+
})
136+
137+
u.Set(key, val)
138+
}()
139+
140+
u.Reset()
141+
runtime.GC()
142+
143+
select {
144+
case <-final:
145+
case <-time.After(time.Second):
146+
t.Fatalf("value is garbage collected")
147+
}
148+
149+
// Keep u alive, otherwise val will always get garbage collected.
150+
u.Set("bar", 1)
151+
}

0 commit comments

Comments
 (0)