Skip to content

Commit bfa8a85

Browse files
doujiang24gopherbot
authored andcommitted
runtime: silently allow pinning of non-Go pointers in runtime.Pinner.Pin
People may not know the details of a pointer, this makes the runtime.Pinner.Pin API easier to use. Fixes #62356 Change-Id: I071df44e01320648a6df5e2a1e65afd6ea52e274 GitHub-Last-Rev: 560a581 GitHub-Pull-Request: #62549 Reviewed-on: https://go-review.googlesource.com/c/go/+/527156 Reviewed-by: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Michael Knyszek <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]>
1 parent b45b001 commit bfa8a85

File tree

2 files changed

+32
-13
lines changed

2 files changed

+32
-13
lines changed

src/runtime/pinner.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,8 @@ type Pinner struct {
2525
// objects, these objects must be pinned separately if they are going to be
2626
// accessed from C code.
2727
//
28-
// The argument must be a pointer of any type or an
29-
// unsafe.Pointer. It must be the result of calling new,
30-
// taking the address of a composite literal, or taking the address of a
31-
// local variable. If one of these conditions is not met, Pin will panic.
28+
// The argument must be a pointer of any type or an unsafe.Pointer.
29+
// It's safe to call Pin on non-Go pointers, in which case Pin will do nothing.
3230
func (p *Pinner) Pin(pointer any) {
3331
if p.pinner == nil {
3432
// Check the pinner cache first.
@@ -59,8 +57,9 @@ func (p *Pinner) Pin(pointer any) {
5957
}
6058
}
6159
ptr := pinnerGetPtr(&pointer)
62-
setPinned(ptr, true)
63-
p.refs = append(p.refs, ptr)
60+
if setPinned(ptr, true) {
61+
p.refs = append(p.refs, ptr)
62+
}
6463
}
6564

6665
// Unpin unpins all pinned objects of the Pinner.
@@ -143,15 +142,19 @@ func isPinned(ptr unsafe.Pointer) bool {
143142
return pinState.isPinned()
144143
}
145144

146-
// setPinned marks or unmarks a Go pointer as pinned.
147-
func setPinned(ptr unsafe.Pointer, pin bool) {
145+
// setPinned marks or unmarks a Go pointer as pinned, when the ptr is a Go pointer.
146+
// It will be ignored while try to pin a non-Go pointer,
147+
// and it will be panic while try to unpin a non-Go pointer,
148+
// which should not happen in normal usage.
149+
func setPinned(ptr unsafe.Pointer, pin bool) bool {
148150
span := spanOfHeap(uintptr(ptr))
149151
if span == nil {
150-
if isGoPointerWithoutSpan(ptr) {
151-
// this is a linker-allocated or zero size object, nothing to do.
152-
return
152+
if !pin {
153+
panic(errorString("tried to unpin non-Go pointer"))
153154
}
154-
panic(errorString("runtime.Pinner.Pin: argument is not a Go pointer"))
155+
// This is a linker-allocated, zero size object or other object,
156+
// nothing to do, silently ignore it.
157+
return false
155158
}
156159

157160
// ensure that the span is swept, b/c sweeping accesses the specials list
@@ -209,7 +212,7 @@ func setPinned(ptr unsafe.Pointer, pin bool) {
209212
}
210213
unlock(&span.speciallock)
211214
releasem(mp)
212-
return
215+
return true
213216
}
214217

215218
type pinState struct {

src/runtime/pinner_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,3 +522,19 @@ func BenchmarkPinnerIsPinnedOnUnpinnedParallel(b *testing.B) {
522522
}
523523
})
524524
}
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+
}

0 commit comments

Comments
 (0)