Source file
src/runtime/pinner.go
1
2
3
4
5 package runtime
6
7 import (
8 "internal/abi"
9 "internal/runtime/atomic"
10 "unsafe"
11 )
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 type Pinner struct {
35 *pinner
36 }
37
38
39
40
41
42
43
44
45
46
47
48
49 func (p *Pinner) Pin(pointer any) {
50 if p.pinner == nil {
51
52 mp := acquirem()
53 if pp := mp.p.ptr(); pp != nil {
54 p.pinner = pp.pinnerCache
55 pp.pinnerCache = nil
56 }
57 releasem(mp)
58
59 if p.pinner == nil {
60
61 p.pinner = new(pinner)
62 p.refs = p.refStore[:0]
63
64
65
66
67
68
69
70 SetFinalizer(p.pinner, func(i *pinner) {
71 if len(i.refs) != 0 {
72 i.unpin()
73 pinnerLeakPanic()
74 }
75 })
76 }
77 }
78 ptr := pinnerGetPtr(&pointer)
79 if setPinned(ptr) {
80 p.refs = append(p.refs, ptr)
81 }
82 }
83
84
85
86 func (p *Pinner) Unpin() {
87 p.pinner.unpin()
88
89 mp := acquirem()
90 if pp := mp.p.ptr(); pp != nil && pp.pinnerCache == nil {
91
92
93
94
95 pp.pinnerCache = p.pinner
96 p.pinner = nil
97 }
98 releasem(mp)
99 }
100
101 const (
102 pinnerSize = 64
103 pinnerRefStoreSize = (pinnerSize - unsafe.Sizeof([]unsafe.Pointer{})) / unsafe.Sizeof(unsafe.Pointer(nil))
104 )
105
106 type pinner struct {
107 refs []unsafe.Pointer
108 refStore [pinnerRefStoreSize]unsafe.Pointer
109 }
110
111 func (p *pinner) unpin() {
112 if p == nil || p.refs == nil {
113 return
114 }
115 for refs := p.refs; len(refs) != 0; {
116 refs = refs[unpinObjects(refs):]
117 }
118
119
120
121 p.refStore = [pinnerRefStoreSize]unsafe.Pointer{}
122 p.refs = p.refStore[:0]
123 }
124
125 func pinnerGetPtr(i *any) unsafe.Pointer {
126 e := efaceOf(i)
127 etyp := e._type
128 if etyp == nil {
129 panic(errorString("runtime.Pinner: argument is nil"))
130 }
131 if kind := etyp.Kind(); kind != abi.Pointer && kind != abi.UnsafePointer {
132 panic(errorString("runtime.Pinner: argument is not a pointer: " + toRType(etyp).string()))
133 }
134 if inUserArenaChunk(uintptr(e.data)) {
135
136 panic(errorString("runtime.Pinner: object was allocated into an arena"))
137 }
138 return e.data
139 }
140
141
142
143
144
145 func isPinned(ptr unsafe.Pointer) bool {
146 span := spanOfHeap(uintptr(ptr))
147 if span == nil {
148
149
150 return true
151 }
152 pinnerBits := span.getPinnerBits()
153
154
155
156 if pinnerBits == nil {
157 return false
158 }
159 objIndex := span.objIndex(uintptr(ptr))
160 pinState := pinnerBits.ofObject(objIndex)
161 KeepAlive(ptr)
162 return pinState.isPinned()
163 }
164
165
166 func setPinned(ptr unsafe.Pointer) bool {
167 span := spanOfHeap(uintptr(ptr))
168 if span == nil {
169
170
171 return false
172 }
173
174
175
176 mp := acquirem()
177 span.ensureSwept()
178 KeepAlive(ptr)
179
180 objIndex := span.objIndex(uintptr(ptr))
181
182 lock(&span.speciallock)
183
184 pinnerBits := span.getPinnerBits()
185 if pinnerBits == nil {
186 pinnerBits = span.newPinnerBits()
187 span.setPinnerBits(pinnerBits)
188 }
189 pinState := pinnerBits.ofObject(objIndex)
190 if pinState.isPinned() {
191
192 pinState.setMultiPinned(true)
193
194 offset := objIndex * span.elemsize
195 span.incPinCounter(offset)
196 } else {
197
198 pinState.setPinned(true)
199 }
200 unlock(&span.speciallock)
201 releasem(mp)
202 return true
203 }
204
205
206
207 func unpinObjects(refs []unsafe.Pointer) int {
208 span := spanOfHeap(uintptr(refs[0]))
209 if span == nil {
210 panic(errorString("tried to unpin non-Go pointer"))
211 }
212 mp := acquirem()
213 span.ensureSwept()
214 lock(&span.speciallock)
215 pinnerBits := span.getPinnerBits()
216 if pinnerBits == nil {
217 throw("runtime.Pinner: object already unpinned")
218 }
219
220
221
222 const maxBatch = 64
223 base, limit := span.base(), span.limit
224 n := 0
225 for n < min(len(refs), maxBatch) {
226 ptr := uintptr(refs[n])
227 if ptr < base || ptr >= limit {
228 break
229 }
230 objIndex := span.objIndex(ptr)
231
232
233 pinState := pinnerBits.ofObject(objIndex)
234 if pinState.isPinned() {
235 if pinState.isMultiPinned() {
236 offset := objIndex * span.elemsize
237 if !span.decPinCounter(offset) {
238
239 pinState.setMultiPinned(false)
240 }
241 } else {
242
243 pinState.setPinned(false)
244 }
245 } else {
246
247 throw("runtime.Pinner: object already unpinned")
248 }
249 n++
250 }
251 unlock(&span.speciallock)
252 releasem(mp)
253 KeepAlive(refs)
254 return n
255 }
256
257 type pinState struct {
258 bytep *uint8
259 byteVal uint8
260 mask uint8
261 }
262
263
264
265
266 func (v *pinState) isPinned() bool {
267 return (v.byteVal & v.mask) != 0
268 }
269
270 func (v *pinState) isMultiPinned() bool {
271 return (v.byteVal & (v.mask << 1)) != 0
272 }
273
274 func (v *pinState) setPinned(val bool) {
275 v.set(val, false)
276 }
277
278 func (v *pinState) setMultiPinned(val bool) {
279 v.set(val, true)
280 }
281
282
283
284
285
286
287 func (v *pinState) set(val bool, multipin bool) {
288 mask := v.mask
289 if multipin {
290 mask <<= 1
291 }
292 if (v.byteVal&mask != 0) == val {
293 return
294 }
295 if val {
296 v.byteVal |= mask
297 } else {
298 v.byteVal &^= mask
299 }
300 atomic.Store8(v.bytep, v.byteVal)
301 }
302
303
304 type pinnerBits gcBits
305
306
307
308
309
310 func (p *pinnerBits) ofObject(n uintptr) pinState {
311 bytep, mask := (*gcBits)(p).bitp(n * 2)
312 byteVal := atomic.Load8(bytep)
313 return pinState{bytep, byteVal, mask}
314 }
315
316 func (s *mspan) pinnerBitSize() uintptr {
317 return divRoundUp(uintptr(s.nelems)*2, 8)
318 }
319
320
321
322
323 func (s *mspan) newPinnerBits() *pinnerBits {
324 return (*pinnerBits)(newMarkBits(uintptr(s.nelems) * 2))
325 }
326
327
328
329
330 func (s *mspan) getPinnerBits() *pinnerBits {
331 return (*pinnerBits)(atomic.Loadp(unsafe.Pointer(&s.pinnerBits)))
332 }
333
334 func (s *mspan) setPinnerBits(p *pinnerBits) {
335 atomicstorep(unsafe.Pointer(&s.pinnerBits), unsafe.Pointer(p))
336 }
337
338
339
340
341 func (s *mspan) refreshPinnerBits() {
342 p := s.getPinnerBits()
343 if p == nil {
344 return
345 }
346
347 hasPins := false
348 bytes := alignUp(s.pinnerBitSize(), 8)
349
350
351
352
353
354 for _, x := range unsafe.Slice((*uint64)(unsafe.Pointer(&p.x)), bytes/8) {
355 if x != 0 {
356 hasPins = true
357 break
358 }
359 }
360
361 if hasPins {
362 newPinnerBits := s.newPinnerBits()
363 memmove(unsafe.Pointer(&newPinnerBits.x), unsafe.Pointer(&p.x), bytes)
364 s.setPinnerBits(newPinnerBits)
365 } else {
366 s.setPinnerBits(nil)
367 }
368 }
369
370
371
372
373
374
375 func (span *mspan) incPinCounter(offset uintptr) {
376 var rec *specialPinCounter
377 ref, exists := span.specialFindSplicePoint(offset, _KindSpecialPinCounter)
378 if !exists {
379 if pp := getg().m.p.ptr(); pp != nil && pp.pinCounterCache != nil {
380 rec = pp.pinCounterCache
381 pp.pinCounterCache = nil
382 } else {
383 lock(&mheap_.speciallock)
384 rec = (*specialPinCounter)(mheap_.specialPinCounterAlloc.alloc())
385 unlock(&mheap_.speciallock)
386 }
387
388 rec.special.offset = offset
389 rec.special.kind = _KindSpecialPinCounter
390 rec.special.next = *ref
391 rec.counter = 1
392 *ref = (*special)(unsafe.Pointer(rec))
393 spanHasSpecials(span)
394 } else {
395 rec = (*specialPinCounter)(unsafe.Pointer(*ref))
396 rec.counter++
397 }
398 }
399
400
401
402
403
404
405 func (span *mspan) decPinCounter(offset uintptr) bool {
406 ref, exists := span.specialFindSplicePoint(offset, _KindSpecialPinCounter)
407 if !exists {
408 throw("runtime.Pinner: decreased non-existing pin counter")
409 }
410 counter := (*specialPinCounter)(unsafe.Pointer(*ref))
411 counter.counter--
412 if counter.counter == 0 {
413 *ref = counter.special.next
414 if span.specials == nil {
415 spanHasNoSpecials(span)
416 }
417 if pp := getg().m.p.ptr(); pp != nil && pp.pinCounterCache == nil {
418 counter.special.next = nil
419 pp.pinCounterCache = counter
420 } else {
421 lock(&mheap_.speciallock)
422 mheap_.specialPinCounterAlloc.free(unsafe.Pointer(counter))
423 unlock(&mheap_.speciallock)
424 }
425 return false
426 }
427 return true
428 }
429
430
431 func pinnerGetPinCounter(addr unsafe.Pointer) *uintptr {
432 _, span, objIndex := findObject(uintptr(addr), 0, 0)
433 offset := objIndex * span.elemsize
434 t, exists := span.specialFindSplicePoint(offset, _KindSpecialPinCounter)
435 if !exists {
436 return nil
437 }
438 counter := (*specialPinCounter)(unsafe.Pointer(*t))
439 return &counter.counter
440 }
441
442
443
444 var pinnerLeakPanic = func() {
445 panic(errorString("runtime.Pinner: found leaking pinned pointer; forgot to call Unpin()?"))
446 }
447
View as plain text