Source file
src/runtime/pinner_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "runtime"
9 "testing"
10 "time"
11 "unsafe"
12 )
13
14 type obj struct {
15 x int64
16 y int64
17 z int64
18 }
19
20 type objWith[T any] struct {
21 x int64
22 y int64
23 z int64
24 o T
25 }
26
27 var (
28 globalUintptr uintptr
29 globalPtrToObj = &obj{}
30 globalPtrToObjWithPtr = &objWith[*uintptr]{}
31 globalPtrToRuntimeObj = func() *obj { return &obj{} }()
32 globalPtrToRuntimeObjWithPtr = func() *objWith[*uintptr] { return &objWith[*uintptr]{} }()
33 )
34
35 func assertDidPanic(t *testing.T) {
36 if recover() == nil {
37 t.Fatal("did not panic")
38 }
39 }
40
41 func assertCgoCheckPanics(t *testing.T, p any) {
42 defer func() {
43 if recover() == nil {
44 t.Fatal("cgoCheckPointer() did not panic, make sure the tests run with cgocheck=1")
45 }
46 }()
47 runtime.CgoCheckPointer(p, true)
48 }
49
50 func TestPinnerSimple(t *testing.T) {
51 var pinner runtime.Pinner
52 p := new(obj)
53 addr := unsafe.Pointer(p)
54 if runtime.IsPinned(addr) {
55 t.Fatal("already marked as pinned")
56 }
57 pinner.Pin(p)
58 if !runtime.IsPinned(addr) {
59 t.Fatal("not marked as pinned")
60 }
61 if runtime.GetPinCounter(addr) != nil {
62 t.Fatal("pin counter should not exist")
63 }
64 pinner.Unpin()
65 if runtime.IsPinned(addr) {
66 t.Fatal("still marked as pinned")
67 }
68 }
69
70 func TestPinnerPinKeepsAliveAndReleases(t *testing.T) {
71 var pinner runtime.Pinner
72 p := new(obj)
73 done := make(chan struct{})
74 runtime.SetFinalizer(p, func(any) {
75 done <- struct{}{}
76 })
77 pinner.Pin(p)
78 p = nil
79 runtime.GC()
80 runtime.GC()
81 select {
82 case <-done:
83 t.Fatal("Pin() didn't keep object alive")
84 case <-time.After(time.Millisecond * 10):
85 break
86 }
87 pinner.Unpin()
88 runtime.GC()
89 runtime.GC()
90 select {
91 case <-done:
92 break
93 case <-time.After(time.Second):
94 t.Fatal("Unpin() didn't release object")
95 }
96 }
97
98 func TestPinnerMultiplePinsSame(t *testing.T) {
99 const N = 100
100 var pinner runtime.Pinner
101 p := new(obj)
102 addr := unsafe.Pointer(p)
103 if runtime.IsPinned(addr) {
104 t.Fatal("already marked as pinned")
105 }
106 for i := 0; i < N; i++ {
107 pinner.Pin(p)
108 }
109 if !runtime.IsPinned(addr) {
110 t.Fatal("not marked as pinned")
111 }
112 if cnt := runtime.GetPinCounter(addr); cnt == nil || *cnt != N-1 {
113 t.Fatalf("pin counter incorrect: %d", *cnt)
114 }
115 pinner.Unpin()
116 if runtime.IsPinned(addr) {
117 t.Fatal("still marked as pinned")
118 }
119 if runtime.GetPinCounter(addr) != nil {
120 t.Fatal("pin counter was not deleted")
121 }
122 }
123
124 func TestPinnerTwoPinner(t *testing.T) {
125 var pinner1, pinner2 runtime.Pinner
126 p := new(obj)
127 addr := unsafe.Pointer(p)
128 if runtime.IsPinned(addr) {
129 t.Fatal("already marked as pinned")
130 }
131 pinner1.Pin(p)
132 if !runtime.IsPinned(addr) {
133 t.Fatal("not marked as pinned")
134 }
135 if runtime.GetPinCounter(addr) != nil {
136 t.Fatal("pin counter should not exist")
137 }
138 pinner2.Pin(p)
139 if !runtime.IsPinned(addr) {
140 t.Fatal("not marked as pinned")
141 }
142 if cnt := runtime.GetPinCounter(addr); cnt == nil || *cnt != 1 {
143 t.Fatalf("pin counter incorrect: %d", *cnt)
144 }
145 pinner1.Unpin()
146 if !runtime.IsPinned(addr) {
147 t.Fatal("not marked as pinned")
148 }
149 if runtime.GetPinCounter(addr) != nil {
150 t.Fatal("pin counter should not exist")
151 }
152 pinner2.Unpin()
153 if runtime.IsPinned(addr) {
154 t.Fatal("still marked as pinned")
155 }
156 if runtime.GetPinCounter(addr) != nil {
157 t.Fatal("pin counter was not deleted")
158 }
159 }
160
161 func TestPinnerPinZerosizeObj(t *testing.T) {
162 var pinner runtime.Pinner
163 defer pinner.Unpin()
164 p := new(struct{})
165 pinner.Pin(p)
166 if !runtime.IsPinned(unsafe.Pointer(p)) {
167 t.Fatal("not marked as pinned")
168 }
169 }
170
171 func TestPinnerPinGlobalPtr(t *testing.T) {
172 var pinner runtime.Pinner
173 defer pinner.Unpin()
174 pinner.Pin(globalPtrToObj)
175 pinner.Pin(globalPtrToObjWithPtr)
176 pinner.Pin(globalPtrToRuntimeObj)
177 pinner.Pin(globalPtrToRuntimeObjWithPtr)
178 }
179
180 func TestPinnerPinTinyObj(t *testing.T) {
181 var pinner runtime.Pinner
182 const N = 64
183 var addr [N]unsafe.Pointer
184 for i := 0; i < N; i++ {
185 p := new(bool)
186 addr[i] = unsafe.Pointer(p)
187 pinner.Pin(p)
188 pinner.Pin(p)
189 if !runtime.IsPinned(addr[i]) {
190 t.Fatalf("not marked as pinned: %d", i)
191 }
192 if cnt := runtime.GetPinCounter(addr[i]); cnt == nil || *cnt == 0 {
193 t.Fatalf("pin counter incorrect: %d, %d", *cnt, i)
194 }
195 }
196 pinner.Unpin()
197 for i := 0; i < N; i++ {
198 if runtime.IsPinned(addr[i]) {
199 t.Fatal("still marked as pinned")
200 }
201 if runtime.GetPinCounter(addr[i]) != nil {
202 t.Fatal("pin counter should not exist")
203 }
204 }
205 }
206
207 func TestPinnerInterface(t *testing.T) {
208 var pinner runtime.Pinner
209 o := new(obj)
210 ifc := any(o)
211 pinner.Pin(&ifc)
212 if !runtime.IsPinned(unsafe.Pointer(&ifc)) {
213 t.Fatal("not marked as pinned")
214 }
215 if runtime.IsPinned(unsafe.Pointer(o)) {
216 t.Fatal("marked as pinned")
217 }
218 pinner.Unpin()
219 pinner.Pin(ifc)
220 if !runtime.IsPinned(unsafe.Pointer(o)) {
221 t.Fatal("not marked as pinned")
222 }
223 if runtime.IsPinned(unsafe.Pointer(&ifc)) {
224 t.Fatal("marked as pinned")
225 }
226 pinner.Unpin()
227 }
228
229 func TestPinnerPinNonPtrPanics(t *testing.T) {
230 var pinner runtime.Pinner
231 defer pinner.Unpin()
232 var i int
233 defer assertDidPanic(t)
234 pinner.Pin(i)
235 }
236
237 func TestPinnerReuse(t *testing.T) {
238 var pinner runtime.Pinner
239 p := new(obj)
240 p2 := &p
241 assertCgoCheckPanics(t, p2)
242 pinner.Pin(p)
243 runtime.CgoCheckPointer(p2, true)
244 pinner.Unpin()
245 assertCgoCheckPanics(t, p2)
246 pinner.Pin(p)
247 runtime.CgoCheckPointer(p2, true)
248 pinner.Unpin()
249 }
250
251 func TestPinnerEmptyUnpin(t *testing.T) {
252 var pinner runtime.Pinner
253 pinner.Unpin()
254 pinner.Unpin()
255 }
256
257 func TestPinnerLeakPanics(t *testing.T) {
258 old := runtime.GetPinnerLeakPanic()
259 func() {
260 defer assertDidPanic(t)
261 old()
262 }()
263 done := make(chan struct{})
264 runtime.SetPinnerLeakPanic(func() {
265 done <- struct{}{}
266 })
267 func() {
268 var pinner runtime.Pinner
269 p := new(obj)
270 pinner.Pin(p)
271 }()
272 runtime.GC()
273 runtime.GC()
274 select {
275 case <-done:
276 break
277 case <-time.After(time.Second):
278 t.Fatal("leak didn't make GC to panic")
279 }
280 runtime.SetPinnerLeakPanic(old)
281 }
282
283 func TestPinnerCgoCheckPtr2Ptr(t *testing.T) {
284 var pinner runtime.Pinner
285 defer pinner.Unpin()
286 p := new(obj)
287 p2 := &objWith[*obj]{o: p}
288 assertCgoCheckPanics(t, p2)
289 pinner.Pin(p)
290 runtime.CgoCheckPointer(p2, true)
291 }
292
293 func TestPinnerCgoCheckPtr2UnsafePtr(t *testing.T) {
294 var pinner runtime.Pinner
295 defer pinner.Unpin()
296 p := unsafe.Pointer(new(obj))
297 p2 := &objWith[unsafe.Pointer]{o: p}
298 assertCgoCheckPanics(t, p2)
299 pinner.Pin(p)
300 runtime.CgoCheckPointer(p2, true)
301 }
302
303 func TestPinnerCgoCheckPtr2UnknownPtr(t *testing.T) {
304 var pinner runtime.Pinner
305 defer pinner.Unpin()
306 p := unsafe.Pointer(new(obj))
307 p2 := &p
308 func() {
309 defer assertDidPanic(t)
310 runtime.CgoCheckPointer(p2, nil)
311 }()
312 pinner.Pin(p)
313 runtime.CgoCheckPointer(p2, nil)
314 }
315
316 func TestPinnerCgoCheckInterface(t *testing.T) {
317 var pinner runtime.Pinner
318 defer pinner.Unpin()
319 var ifc any
320 var o obj
321 ifc = &o
322 p := &ifc
323 assertCgoCheckPanics(t, p)
324 pinner.Pin(&o)
325 runtime.CgoCheckPointer(p, true)
326 }
327
328 func TestPinnerCgoCheckSlice(t *testing.T) {
329 var pinner runtime.Pinner
330 defer pinner.Unpin()
331 sl := []int{1, 2, 3}
332 assertCgoCheckPanics(t, &sl)
333 pinner.Pin(&sl[0])
334 runtime.CgoCheckPointer(&sl, true)
335 }
336
337 func TestPinnerCgoCheckString(t *testing.T) {
338 var pinner runtime.Pinner
339 defer pinner.Unpin()
340 b := []byte("foobar")
341 str := unsafe.String(&b[0], 6)
342 assertCgoCheckPanics(t, &str)
343 pinner.Pin(&b[0])
344 runtime.CgoCheckPointer(&str, true)
345 }
346
347 func TestPinnerCgoCheckPinned2UnpinnedPanics(t *testing.T) {
348 var pinner runtime.Pinner
349 defer pinner.Unpin()
350 p := new(obj)
351 p2 := &objWith[*obj]{o: p}
352 assertCgoCheckPanics(t, p2)
353 pinner.Pin(p2)
354 assertCgoCheckPanics(t, p2)
355 }
356
357 func TestPinnerCgoCheckPtr2Pinned2Unpinned(t *testing.T) {
358 var pinner runtime.Pinner
359 defer pinner.Unpin()
360 p := new(obj)
361 p2 := &objWith[*obj]{o: p}
362 p3 := &objWith[*objWith[*obj]]{o: p2}
363 assertCgoCheckPanics(t, p2)
364 assertCgoCheckPanics(t, p3)
365 pinner.Pin(p2)
366 assertCgoCheckPanics(t, p2)
367 assertCgoCheckPanics(t, p3)
368 pinner.Pin(p)
369 runtime.CgoCheckPointer(p2, true)
370 runtime.CgoCheckPointer(p3, true)
371 }
372
373 func BenchmarkPinnerPinUnpinBatch(b *testing.B) {
374 const Batch = 1000
375 var data [Batch]*obj
376 for i := 0; i < Batch; i++ {
377 data[i] = new(obj)
378 }
379 b.ResetTimer()
380 for n := 0; n < b.N; n++ {
381 var pinner runtime.Pinner
382 for i := 0; i < Batch; i++ {
383 pinner.Pin(data[i])
384 }
385 pinner.Unpin()
386 }
387 }
388
389 func BenchmarkPinnerPinUnpinBatchDouble(b *testing.B) {
390 const Batch = 1000
391 var data [Batch]*obj
392 for i := 0; i < Batch; i++ {
393 data[i] = new(obj)
394 }
395 b.ResetTimer()
396 for n := 0; n < b.N; n++ {
397 var pinner runtime.Pinner
398 for i := 0; i < Batch; i++ {
399 pinner.Pin(data[i])
400 pinner.Pin(data[i])
401 }
402 pinner.Unpin()
403 }
404 }
405
406 func BenchmarkPinnerPinUnpinBatchTiny(b *testing.B) {
407 const Batch = 1000
408 var data [Batch]*bool
409 for i := 0; i < Batch; i++ {
410 data[i] = new(bool)
411 }
412 b.ResetTimer()
413 for n := 0; n < b.N; n++ {
414 var pinner runtime.Pinner
415 for i := 0; i < Batch; i++ {
416 pinner.Pin(data[i])
417 }
418 pinner.Unpin()
419 }
420 }
421
422 func BenchmarkPinnerPinUnpin(b *testing.B) {
423 p := new(obj)
424 for n := 0; n < b.N; n++ {
425 var pinner runtime.Pinner
426 pinner.Pin(p)
427 pinner.Unpin()
428 }
429 }
430
431 func BenchmarkPinnerPinUnpinTiny(b *testing.B) {
432 p := new(bool)
433 for n := 0; n < b.N; n++ {
434 var pinner runtime.Pinner
435 pinner.Pin(p)
436 pinner.Unpin()
437 }
438 }
439
440 func BenchmarkPinnerPinUnpinDouble(b *testing.B) {
441 p := new(obj)
442 for n := 0; n < b.N; n++ {
443 var pinner runtime.Pinner
444 pinner.Pin(p)
445 pinner.Pin(p)
446 pinner.Unpin()
447 }
448 }
449
450 func BenchmarkPinnerPinUnpinParallel(b *testing.B) {
451 b.RunParallel(func(pb *testing.PB) {
452 p := new(obj)
453 for pb.Next() {
454 var pinner runtime.Pinner
455 pinner.Pin(p)
456 pinner.Unpin()
457 }
458 })
459 }
460
461 func BenchmarkPinnerPinUnpinParallelTiny(b *testing.B) {
462 b.RunParallel(func(pb *testing.PB) {
463 p := new(bool)
464 for pb.Next() {
465 var pinner runtime.Pinner
466 pinner.Pin(p)
467 pinner.Unpin()
468 }
469 })
470 }
471
472 func BenchmarkPinnerPinUnpinParallelDouble(b *testing.B) {
473 b.RunParallel(func(pb *testing.PB) {
474 p := new(obj)
475 for pb.Next() {
476 var pinner runtime.Pinner
477 pinner.Pin(p)
478 pinner.Pin(p)
479 pinner.Unpin()
480 }
481 })
482 }
483
484 func BenchmarkPinnerIsPinnedOnPinned(b *testing.B) {
485 var pinner runtime.Pinner
486 ptr := new(obj)
487 pinner.Pin(ptr)
488 b.ResetTimer()
489 for n := 0; n < b.N; n++ {
490 runtime.IsPinned(unsafe.Pointer(ptr))
491 }
492 pinner.Unpin()
493 }
494
495 func BenchmarkPinnerIsPinnedOnUnpinned(b *testing.B) {
496 ptr := new(obj)
497 b.ResetTimer()
498 for n := 0; n < b.N; n++ {
499 runtime.IsPinned(unsafe.Pointer(ptr))
500 }
501 }
502
503 func BenchmarkPinnerIsPinnedOnPinnedParallel(b *testing.B) {
504 var pinner runtime.Pinner
505 ptr := new(obj)
506 pinner.Pin(ptr)
507 b.ResetTimer()
508 b.RunParallel(func(pb *testing.PB) {
509 for pb.Next() {
510 runtime.IsPinned(unsafe.Pointer(ptr))
511 }
512 })
513 pinner.Unpin()
514 }
515
516 func BenchmarkPinnerIsPinnedOnUnpinnedParallel(b *testing.B) {
517 ptr := new(obj)
518 b.ResetTimer()
519 b.RunParallel(func(pb *testing.PB) {
520 for pb.Next() {
521 runtime.IsPinned(unsafe.Pointer(ptr))
522 }
523 })
524 }
525
526
527 func TestPinnerConstStringData(t *testing.T) {
528 var pinner runtime.Pinner
529 str := "test-const-string"
530 p := unsafe.StringData(str)
531 addr := unsafe.Pointer(p)
532 if !runtime.IsPinned(addr) {
533 t.Fatal("not marked as pinned")
534 }
535 pinner.Pin(p)
536 pinner.Unpin()
537 if !runtime.IsPinned(addr) {
538 t.Fatal("not marked as pinned")
539 }
540 }
541
View as plain text