github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/runtime/pinner_test.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 // const string data is not in span. 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 }