// having pages get stuck on them. These pages are hidden from
// the scavenger, so in small idle heaps a significant amount
// of additional memory might be held onto.
+ //
+ // Also, flush the pinner cache, to avoid leaking that memory
+ // indefinitely.
systemstack(func() {
forEachP(func(pp *p) {
pp.mcache.prepareForSweep()
unlock(&mheap_.lock)
})
}
+ pp.pinnerCache = nil
})
})
// Now that we've swept stale spans in mcaches, they don't
// local variable. If one of these conditions is not met, Pin will panic.
func (p *Pinner) Pin(pointer any) {
if p.pinner == nil {
- p.pinner = new(pinner)
- p.refs = p.refStore[:0]
- SetFinalizer(p.pinner, func(i *pinner) {
- if len(i.refs) != 0 {
- i.unpin() // only required to make the test idempotent
- pinnerLeakPanic()
- }
- })
+ // Check the pinner cache first.
+ mp := acquirem()
+ if pp := mp.p.ptr(); pp != nil {
+ p.pinner = pp.pinnerCache
+ pp.pinnerCache = nil
+ }
+ releasem(mp)
+
+ if p.pinner == nil {
+ // Didn't get anything from the pinner cache.
+ p.pinner = new(pinner)
+ p.refs = p.refStore[:0]
+
+ // We set this finalizer once and never clear it. Thus, if the
+ // pinner gets cached, we'll reuse it, along with its finalizer.
+ // This lets us avoid the relatively expensive SetFinalizer call
+ // when reusing from the cache. The finalizer however has to be
+ // resilient to an empty pinner being finalized, which is done
+ // by checking p.refs' length.
+ SetFinalizer(p.pinner, func(i *pinner) {
+ if len(i.refs) != 0 {
+ i.unpin() // only required to make the test idempotent
+ pinnerLeakPanic()
+ }
+ })
+ }
}
ptr := pinnerGetPtr(&pointer)
-
setPinned(ptr, true)
p.refs = append(p.refs, ptr)
}
// Unpin all pinned objects of the Pinner.
func (p *Pinner) Unpin() {
p.pinner.unpin()
+
+ mp := acquirem()
+ if pp := mp.p.ptr(); pp != nil && pp.pinnerCache == nil {
+ // Put the pinner back in the cache, but only if the
+ // cache is empty. If application code is reusing Pinners
+ // on its own, we want to leave the backing store in place
+ // so reuse is more efficient.
+ pp.pinnerCache = p.pinner
+ p.pinner = nil
+ }
+ releasem(mp)
}
const (
}
for i := range p.refs {
setPinned(p.refs[i], false)
- p.refs[i] = nil
}
+ // The following two lines make all pointers to references
+ // in p.refs unreachable, either by deleting them or dropping
+ // p.refs' backing store (if it was not backed by refStore).
p.refStore = [pinnerRefStoreSize]unsafe.Pointer{}
p.refs = p.refStore[:0]
}