Source file
src/weak/pointer_test.go
1
2
3
4
5 package weak_test
6
7 import (
8 "context"
9 "internal/goarch"
10 "runtime"
11 "sync"
12 "testing"
13 "time"
14 "unsafe"
15 "weak"
16 )
17
18 type T struct {
19
20
21
22
23 t *T
24 a int
25 b int
26 }
27
28 func TestPointer(t *testing.T) {
29 var zero weak.Pointer[T]
30 if zero.Value() != nil {
31 t.Error("Value of zero value of weak.Pointer is not nil")
32 }
33 zeroNil := weak.Make[T](nil)
34 if zeroNil.Value() != nil {
35 t.Error("Value of weak.Make[T](nil) is not nil")
36 }
37
38 bt := new(T)
39 wt := weak.Make(bt)
40 if st := wt.Value(); st != bt {
41 t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt)
42 }
43
44 runtime.GC()
45
46 if st := wt.Value(); st != bt {
47 t.Fatalf("weak pointer is not the same as strong pointer after GC: %p vs. %p", st, bt)
48 }
49
50 runtime.GC()
51
52 if st := wt.Value(); st != nil {
53 t.Fatalf("expected weak pointer to be nil, got %p", st)
54 }
55 }
56
57 func TestPointerEquality(t *testing.T) {
58 var zero weak.Pointer[T]
59 zeroNil := weak.Make[T](nil)
60 if zero != zeroNil {
61 t.Error("weak.Make[T](nil) != zero value of weak.Pointer[T]")
62 }
63
64 bt := make([]*T, 10)
65 wt := make([]weak.Pointer[T], 10)
66 wo := make([]weak.Pointer[int], 10)
67 for i := range bt {
68 bt[i] = new(T)
69 wt[i] = weak.Make(bt[i])
70 wo[i] = weak.Make(&bt[i].a)
71 }
72 for i := range bt {
73 st := wt[i].Value()
74 if st != bt[i] {
75 t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt[i])
76 }
77 if wp := weak.Make(st); wp != wt[i] {
78 t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wt[i])
79 }
80 if wp := weak.Make(&st.a); wp != wo[i] {
81 t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wo[i])
82 }
83 if i == 0 {
84 continue
85 }
86 if wt[i] == wt[i-1] {
87 t.Fatalf("expected weak pointers to not be equal to each other, but got %v", wt[i])
88 }
89 }
90
91 runtime.GC()
92 for i := range bt {
93 st := wt[i].Value()
94 if st != bt[i] {
95 t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt[i])
96 }
97 if wp := weak.Make(st); wp != wt[i] {
98 t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wt[i])
99 }
100 if wp := weak.Make(&st.a); wp != wo[i] {
101 t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wo[i])
102 }
103 if i == 0 {
104 continue
105 }
106 if wt[i] == wt[i-1] {
107 t.Fatalf("expected weak pointers to not be equal to each other, but got %v", wt[i])
108 }
109 }
110 bt = nil
111
112 runtime.GC()
113 for i := range wt {
114 st := wt[i].Value()
115 if st != nil {
116 t.Fatalf("expected weak pointer to be nil, got %p", st)
117 }
118 if i == 0 {
119 continue
120 }
121 if wt[i] == wt[i-1] {
122 t.Fatalf("expected weak pointers to not be equal to each other, but got %v", wt[i])
123 }
124 }
125 }
126
127 func TestPointerFinalizer(t *testing.T) {
128 bt := new(T)
129 wt := weak.Make(bt)
130 done := make(chan struct{}, 1)
131 runtime.SetFinalizer(bt, func(bt *T) {
132 if wt.Value() != nil {
133 t.Errorf("weak pointer did not go nil before finalizer ran")
134 }
135 done <- struct{}{}
136 })
137
138
139 runtime.GC()
140 if wt.Value() == nil {
141 t.Errorf("weak pointer went nil too soon")
142 }
143 runtime.KeepAlive(bt)
144
145
146
147
148 runtime.GC()
149 if wt.Value() != nil {
150 t.Errorf("weak pointer did not go nil when finalizer was enqueued")
151 }
152
153
154 <-done
155
156
157 runtime.GC()
158 if wt.Value() != nil {
159 t.Errorf("weak pointer is non-nil even after finalization: %v", wt)
160 }
161 }
162
163 func TestPointerCleanup(t *testing.T) {
164 bt := new(T)
165 wt := weak.Make(bt)
166 done := make(chan struct{}, 1)
167 runtime.AddCleanup(bt, func(_ bool) {
168 if wt.Value() != nil {
169 t.Errorf("weak pointer did not go nil before cleanup was executed")
170 }
171 done <- struct{}{}
172 }, true)
173
174
175 runtime.GC()
176 if wt.Value() == nil {
177 t.Errorf("weak pointer went nil too soon")
178 }
179 runtime.KeepAlive(bt)
180
181
182
183
184 runtime.GC()
185 if wt.Value() != nil {
186 t.Errorf("weak pointer did not go nil when cleanup was enqueued")
187 }
188
189
190 <-done
191
192
193 runtime.GC()
194 if wt.Value() != nil {
195 t.Errorf("weak pointer is non-nil even after cleanup: %v", wt)
196 }
197 }
198
199 func TestPointerSize(t *testing.T) {
200 var p weak.Pointer[T]
201 size := unsafe.Sizeof(p)
202 if size != goarch.PtrSize {
203 t.Errorf("weak.Pointer[T] size = %d, want %d", size, goarch.PtrSize)
204 }
205 }
206
207
208
209
210
211
212
213
214 func TestIssue69210(t *testing.T) {
215 if testing.Short() {
216 t.Skip("this is a stress test that takes seconds to run on its own")
217 }
218 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
219 defer cancel()
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243 var wg sync.WaitGroup
244 wg.Add(1)
245 go func() {
246 defer wg.Done()
247 for {
248 runtime.GC()
249
250 select {
251 case <-ctx.Done():
252 return
253 default:
254 }
255 }
256 }()
257 for range max(runtime.GOMAXPROCS(-1)-1, 1) {
258 wg.Add(1)
259 go func() {
260 defer wg.Done()
261 for {
262 for range 5 {
263 bt := new(T)
264 wt := weak.Make(bt)
265 bt = nil
266 time.Sleep(1 * time.Millisecond)
267 bt = wt.Value()
268 if bt != nil {
269 time.Sleep(4 * time.Millisecond)
270 bt.t = bt
271 bt.a = 12
272 }
273 runtime.KeepAlive(bt)
274 }
275 select {
276 case <-ctx.Done():
277 return
278 default:
279 }
280 }
281 }()
282 }
283 wg.Wait()
284 }
285
286 func TestIssue70739(t *testing.T) {
287 x := make([]*int, 4<<16)
288 wx1 := weak.Make(&x[1<<16])
289 wx2 := weak.Make(&x[1<<16])
290 if wx1 != wx2 {
291 t.Fatal("failed to look up special and made duplicate weak handle; see issue #70739")
292 }
293 }
294
295 var immortal T
296
297 func TestImmortalPointer(t *testing.T) {
298 w0 := weak.Make(&immortal)
299 if weak.Make(&immortal) != w0 {
300 t.Error("immortal weak pointers to the same pointer not equal")
301 }
302 w0a := weak.Make(&immortal.a)
303 w0b := weak.Make(&immortal.b)
304 if w0a == w0b {
305 t.Error("separate immortal pointers (same object) have the same pointer")
306 }
307 if got, want := w0.Value(), &immortal; got != want {
308 t.Errorf("immortal weak pointer to %p has unexpected Value %p", want, got)
309 }
310 if got, want := w0a.Value(), &immortal.a; got != want {
311 t.Errorf("immortal weak pointer to %p has unexpected Value %p", want, got)
312 }
313 if got, want := w0b.Value(), &immortal.b; got != want {
314 t.Errorf("immortal weak pointer to %p has unexpected Value %p", want, got)
315 }
316
317
318 runtime.GC()
319 runtime.GC()
320
321
322 if got, want := w0.Value(), &immortal; got != want {
323 t.Errorf("immortal weak pointer to %p has unexpected Value %p", want, got)
324 }
325 if got, want := w0a.Value(), &immortal.a; got != want {
326 t.Errorf("immortal weak pointer to %p has unexpected Value %p", want, got)
327 }
328 if got, want := w0b.Value(), &immortal.b; got != want {
329 t.Errorf("immortal weak pointer to %p has unexpected Value %p", want, got)
330 }
331 }
332
333 func TestPointerTiny(t *testing.T) {
334 runtime.GC()
335
336 const N = 1000
337 wps := make([]weak.Pointer[int], N)
338 for i := range N {
339
340
341
342 x := new(int)
343 *x = i
344 wps[i] = weak.Make(x)
345 }
346
347
348 runtime.GC()
349
350
351
352
353
354
355 n := 0
356 for _, wp := range wps {
357 if wp.Value() == nil {
358 n++
359 }
360 }
361 const want = 3 * N / 4
362 if n < want {
363 t.Fatalf("not enough weak pointers are nil: expected at least %v, got %v", want, n)
364 }
365 }
366
View as plain text