Initial commit: Go 1.23 release state
This commit is contained in:
540
src/runtime/pinner_test.go
Normal file
540
src/runtime/pinner_test.go
Normal file
@@ -0,0 +1,540 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type obj struct {
|
||||
x int64
|
||||
y int64
|
||||
z int64
|
||||
}
|
||||
|
||||
type objWith[T any] struct {
|
||||
x int64
|
||||
y int64
|
||||
z int64
|
||||
o T
|
||||
}
|
||||
|
||||
var (
|
||||
globalUintptr uintptr
|
||||
globalPtrToObj = &obj{}
|
||||
globalPtrToObjWithPtr = &objWith[*uintptr]{}
|
||||
globalPtrToRuntimeObj = func() *obj { return &obj{} }()
|
||||
globalPtrToRuntimeObjWithPtr = func() *objWith[*uintptr] { return &objWith[*uintptr]{} }()
|
||||
)
|
||||
|
||||
func assertDidPanic(t *testing.T) {
|
||||
if recover() == nil {
|
||||
t.Fatal("did not panic")
|
||||
}
|
||||
}
|
||||
|
||||
func assertCgoCheckPanics(t *testing.T, p any) {
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Fatal("cgoCheckPointer() did not panic, make sure the tests run with cgocheck=1")
|
||||
}
|
||||
}()
|
||||
runtime.CgoCheckPointer(p, true)
|
||||
}
|
||||
|
||||
func TestPinnerSimple(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
p := new(obj)
|
||||
addr := unsafe.Pointer(p)
|
||||
if runtime.IsPinned(addr) {
|
||||
t.Fatal("already marked as pinned")
|
||||
}
|
||||
pinner.Pin(p)
|
||||
if !runtime.IsPinned(addr) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
if runtime.GetPinCounter(addr) != nil {
|
||||
t.Fatal("pin counter should not exist")
|
||||
}
|
||||
pinner.Unpin()
|
||||
if runtime.IsPinned(addr) {
|
||||
t.Fatal("still marked as pinned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPinnerPinKeepsAliveAndReleases(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
p := new(obj)
|
||||
done := make(chan struct{})
|
||||
runtime.SetFinalizer(p, func(any) {
|
||||
done <- struct{}{}
|
||||
})
|
||||
pinner.Pin(p)
|
||||
p = nil
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
select {
|
||||
case <-done:
|
||||
t.Fatal("Pin() didn't keep object alive")
|
||||
case <-time.After(time.Millisecond * 10):
|
||||
break
|
||||
}
|
||||
pinner.Unpin()
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
select {
|
||||
case <-done:
|
||||
break
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("Unpin() didn't release object")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPinnerMultiplePinsSame(t *testing.T) {
|
||||
const N = 100
|
||||
var pinner runtime.Pinner
|
||||
p := new(obj)
|
||||
addr := unsafe.Pointer(p)
|
||||
if runtime.IsPinned(addr) {
|
||||
t.Fatal("already marked as pinned")
|
||||
}
|
||||
for i := 0; i < N; i++ {
|
||||
pinner.Pin(p)
|
||||
}
|
||||
if !runtime.IsPinned(addr) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
if cnt := runtime.GetPinCounter(addr); cnt == nil || *cnt != N-1 {
|
||||
t.Fatalf("pin counter incorrect: %d", *cnt)
|
||||
}
|
||||
pinner.Unpin()
|
||||
if runtime.IsPinned(addr) {
|
||||
t.Fatal("still marked as pinned")
|
||||
}
|
||||
if runtime.GetPinCounter(addr) != nil {
|
||||
t.Fatal("pin counter was not deleted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPinnerTwoPinner(t *testing.T) {
|
||||
var pinner1, pinner2 runtime.Pinner
|
||||
p := new(obj)
|
||||
addr := unsafe.Pointer(p)
|
||||
if runtime.IsPinned(addr) {
|
||||
t.Fatal("already marked as pinned")
|
||||
}
|
||||
pinner1.Pin(p)
|
||||
if !runtime.IsPinned(addr) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
if runtime.GetPinCounter(addr) != nil {
|
||||
t.Fatal("pin counter should not exist")
|
||||
}
|
||||
pinner2.Pin(p)
|
||||
if !runtime.IsPinned(addr) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
if cnt := runtime.GetPinCounter(addr); cnt == nil || *cnt != 1 {
|
||||
t.Fatalf("pin counter incorrect: %d", *cnt)
|
||||
}
|
||||
pinner1.Unpin()
|
||||
if !runtime.IsPinned(addr) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
if runtime.GetPinCounter(addr) != nil {
|
||||
t.Fatal("pin counter should not exist")
|
||||
}
|
||||
pinner2.Unpin()
|
||||
if runtime.IsPinned(addr) {
|
||||
t.Fatal("still marked as pinned")
|
||||
}
|
||||
if runtime.GetPinCounter(addr) != nil {
|
||||
t.Fatal("pin counter was not deleted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPinnerPinZerosizeObj(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
p := new(struct{})
|
||||
pinner.Pin(p)
|
||||
if !runtime.IsPinned(unsafe.Pointer(p)) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPinnerPinGlobalPtr(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
pinner.Pin(globalPtrToObj)
|
||||
pinner.Pin(globalPtrToObjWithPtr)
|
||||
pinner.Pin(globalPtrToRuntimeObj)
|
||||
pinner.Pin(globalPtrToRuntimeObjWithPtr)
|
||||
}
|
||||
|
||||
func TestPinnerPinTinyObj(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
const N = 64
|
||||
var addr [N]unsafe.Pointer
|
||||
for i := 0; i < N; i++ {
|
||||
p := new(bool)
|
||||
addr[i] = unsafe.Pointer(p)
|
||||
pinner.Pin(p)
|
||||
pinner.Pin(p)
|
||||
if !runtime.IsPinned(addr[i]) {
|
||||
t.Fatalf("not marked as pinned: %d", i)
|
||||
}
|
||||
if cnt := runtime.GetPinCounter(addr[i]); cnt == nil || *cnt == 0 {
|
||||
t.Fatalf("pin counter incorrect: %d, %d", *cnt, i)
|
||||
}
|
||||
}
|
||||
pinner.Unpin()
|
||||
for i := 0; i < N; i++ {
|
||||
if runtime.IsPinned(addr[i]) {
|
||||
t.Fatal("still marked as pinned")
|
||||
}
|
||||
if runtime.GetPinCounter(addr[i]) != nil {
|
||||
t.Fatal("pin counter should not exist")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPinnerInterface(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
o := new(obj)
|
||||
ifc := any(o)
|
||||
pinner.Pin(&ifc)
|
||||
if !runtime.IsPinned(unsafe.Pointer(&ifc)) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
if runtime.IsPinned(unsafe.Pointer(o)) {
|
||||
t.Fatal("marked as pinned")
|
||||
}
|
||||
pinner.Unpin()
|
||||
pinner.Pin(ifc)
|
||||
if !runtime.IsPinned(unsafe.Pointer(o)) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
if runtime.IsPinned(unsafe.Pointer(&ifc)) {
|
||||
t.Fatal("marked as pinned")
|
||||
}
|
||||
pinner.Unpin()
|
||||
}
|
||||
|
||||
func TestPinnerPinNonPtrPanics(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
var i int
|
||||
defer assertDidPanic(t)
|
||||
pinner.Pin(i)
|
||||
}
|
||||
|
||||
func TestPinnerReuse(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
p := new(obj)
|
||||
p2 := &p
|
||||
assertCgoCheckPanics(t, p2)
|
||||
pinner.Pin(p)
|
||||
runtime.CgoCheckPointer(p2, true)
|
||||
pinner.Unpin()
|
||||
assertCgoCheckPanics(t, p2)
|
||||
pinner.Pin(p)
|
||||
runtime.CgoCheckPointer(p2, true)
|
||||
pinner.Unpin()
|
||||
}
|
||||
|
||||
func TestPinnerEmptyUnpin(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
pinner.Unpin()
|
||||
pinner.Unpin()
|
||||
}
|
||||
|
||||
func TestPinnerLeakPanics(t *testing.T) {
|
||||
old := runtime.GetPinnerLeakPanic()
|
||||
func() {
|
||||
defer assertDidPanic(t)
|
||||
old()
|
||||
}()
|
||||
done := make(chan struct{})
|
||||
runtime.SetPinnerLeakPanic(func() {
|
||||
done <- struct{}{}
|
||||
})
|
||||
func() {
|
||||
var pinner runtime.Pinner
|
||||
p := new(obj)
|
||||
pinner.Pin(p)
|
||||
}()
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
select {
|
||||
case <-done:
|
||||
break
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("leak didn't make GC to panic")
|
||||
}
|
||||
runtime.SetPinnerLeakPanic(old)
|
||||
}
|
||||
|
||||
func TestPinnerCgoCheckPtr2Ptr(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
p := new(obj)
|
||||
p2 := &objWith[*obj]{o: p}
|
||||
assertCgoCheckPanics(t, p2)
|
||||
pinner.Pin(p)
|
||||
runtime.CgoCheckPointer(p2, true)
|
||||
}
|
||||
|
||||
func TestPinnerCgoCheckPtr2UnsafePtr(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
p := unsafe.Pointer(new(obj))
|
||||
p2 := &objWith[unsafe.Pointer]{o: p}
|
||||
assertCgoCheckPanics(t, p2)
|
||||
pinner.Pin(p)
|
||||
runtime.CgoCheckPointer(p2, true)
|
||||
}
|
||||
|
||||
func TestPinnerCgoCheckPtr2UnknownPtr(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
p := unsafe.Pointer(new(obj))
|
||||
p2 := &p
|
||||
func() {
|
||||
defer assertDidPanic(t)
|
||||
runtime.CgoCheckPointer(p2, nil)
|
||||
}()
|
||||
pinner.Pin(p)
|
||||
runtime.CgoCheckPointer(p2, nil)
|
||||
}
|
||||
|
||||
func TestPinnerCgoCheckInterface(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
var ifc any
|
||||
var o obj
|
||||
ifc = &o
|
||||
p := &ifc
|
||||
assertCgoCheckPanics(t, p)
|
||||
pinner.Pin(&o)
|
||||
runtime.CgoCheckPointer(p, true)
|
||||
}
|
||||
|
||||
func TestPinnerCgoCheckSlice(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
sl := []int{1, 2, 3}
|
||||
assertCgoCheckPanics(t, &sl)
|
||||
pinner.Pin(&sl[0])
|
||||
runtime.CgoCheckPointer(&sl, true)
|
||||
}
|
||||
|
||||
func TestPinnerCgoCheckString(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
b := []byte("foobar")
|
||||
str := unsafe.String(&b[0], 6)
|
||||
assertCgoCheckPanics(t, &str)
|
||||
pinner.Pin(&b[0])
|
||||
runtime.CgoCheckPointer(&str, true)
|
||||
}
|
||||
|
||||
func TestPinnerCgoCheckPinned2UnpinnedPanics(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
p := new(obj)
|
||||
p2 := &objWith[*obj]{o: p}
|
||||
assertCgoCheckPanics(t, p2)
|
||||
pinner.Pin(p2)
|
||||
assertCgoCheckPanics(t, p2)
|
||||
}
|
||||
|
||||
func TestPinnerCgoCheckPtr2Pinned2Unpinned(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
defer pinner.Unpin()
|
||||
p := new(obj)
|
||||
p2 := &objWith[*obj]{o: p}
|
||||
p3 := &objWith[*objWith[*obj]]{o: p2}
|
||||
assertCgoCheckPanics(t, p2)
|
||||
assertCgoCheckPanics(t, p3)
|
||||
pinner.Pin(p2)
|
||||
assertCgoCheckPanics(t, p2)
|
||||
assertCgoCheckPanics(t, p3)
|
||||
pinner.Pin(p)
|
||||
runtime.CgoCheckPointer(p2, true)
|
||||
runtime.CgoCheckPointer(p3, true)
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpinBatch(b *testing.B) {
|
||||
const Batch = 1000
|
||||
var data [Batch]*obj
|
||||
for i := 0; i < Batch; i++ {
|
||||
data[i] = new(obj)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
var pinner runtime.Pinner
|
||||
for i := 0; i < Batch; i++ {
|
||||
pinner.Pin(data[i])
|
||||
}
|
||||
pinner.Unpin()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpinBatchDouble(b *testing.B) {
|
||||
const Batch = 1000
|
||||
var data [Batch]*obj
|
||||
for i := 0; i < Batch; i++ {
|
||||
data[i] = new(obj)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
var pinner runtime.Pinner
|
||||
for i := 0; i < Batch; i++ {
|
||||
pinner.Pin(data[i])
|
||||
pinner.Pin(data[i])
|
||||
}
|
||||
pinner.Unpin()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpinBatchTiny(b *testing.B) {
|
||||
const Batch = 1000
|
||||
var data [Batch]*bool
|
||||
for i := 0; i < Batch; i++ {
|
||||
data[i] = new(bool)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
var pinner runtime.Pinner
|
||||
for i := 0; i < Batch; i++ {
|
||||
pinner.Pin(data[i])
|
||||
}
|
||||
pinner.Unpin()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpin(b *testing.B) {
|
||||
p := new(obj)
|
||||
for n := 0; n < b.N; n++ {
|
||||
var pinner runtime.Pinner
|
||||
pinner.Pin(p)
|
||||
pinner.Unpin()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpinTiny(b *testing.B) {
|
||||
p := new(bool)
|
||||
for n := 0; n < b.N; n++ {
|
||||
var pinner runtime.Pinner
|
||||
pinner.Pin(p)
|
||||
pinner.Unpin()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpinDouble(b *testing.B) {
|
||||
p := new(obj)
|
||||
for n := 0; n < b.N; n++ {
|
||||
var pinner runtime.Pinner
|
||||
pinner.Pin(p)
|
||||
pinner.Pin(p)
|
||||
pinner.Unpin()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpinParallel(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
p := new(obj)
|
||||
for pb.Next() {
|
||||
var pinner runtime.Pinner
|
||||
pinner.Pin(p)
|
||||
pinner.Unpin()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpinParallelTiny(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
p := new(bool)
|
||||
for pb.Next() {
|
||||
var pinner runtime.Pinner
|
||||
pinner.Pin(p)
|
||||
pinner.Unpin()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkPinnerPinUnpinParallelDouble(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
p := new(obj)
|
||||
for pb.Next() {
|
||||
var pinner runtime.Pinner
|
||||
pinner.Pin(p)
|
||||
pinner.Pin(p)
|
||||
pinner.Unpin()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkPinnerIsPinnedOnPinned(b *testing.B) {
|
||||
var pinner runtime.Pinner
|
||||
ptr := new(obj)
|
||||
pinner.Pin(ptr)
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
runtime.IsPinned(unsafe.Pointer(ptr))
|
||||
}
|
||||
pinner.Unpin()
|
||||
}
|
||||
|
||||
func BenchmarkPinnerIsPinnedOnUnpinned(b *testing.B) {
|
||||
ptr := new(obj)
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
runtime.IsPinned(unsafe.Pointer(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPinnerIsPinnedOnPinnedParallel(b *testing.B) {
|
||||
var pinner runtime.Pinner
|
||||
ptr := new(obj)
|
||||
pinner.Pin(ptr)
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
runtime.IsPinned(unsafe.Pointer(ptr))
|
||||
}
|
||||
})
|
||||
pinner.Unpin()
|
||||
}
|
||||
|
||||
func BenchmarkPinnerIsPinnedOnUnpinnedParallel(b *testing.B) {
|
||||
ptr := new(obj)
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
runtime.IsPinned(unsafe.Pointer(ptr))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// const string data is not in span.
|
||||
func TestPinnerConstStringData(t *testing.T) {
|
||||
var pinner runtime.Pinner
|
||||
str := "test-const-string"
|
||||
p := unsafe.StringData(str)
|
||||
addr := unsafe.Pointer(p)
|
||||
if !runtime.IsPinned(addr) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
pinner.Pin(p)
|
||||
pinner.Unpin()
|
||||
if !runtime.IsPinned(addr) {
|
||||
t.Fatal("not marked as pinned")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user