sync.Pool Under the Hood
sync.Pool takes large chunks of contiguous memory. Since it needs thread safety to store and retrieve these memory objects, the lock granularity is much smaller when competing on a few Ps than when many Gs compete at once. To reduce competition further, each P has its own private memory section. Multiple contiguous memory pools use mutex locks per P for exclusive access. Each P has a public pool that other Ps can steal from. When taking elements, the process first checks internal storage for lock-free retrieval. But this requires pinning the P to prevent the scheduler from interrupting.
// Local per-P Pool appendix.
type poolLocalInternal struct {
private interface{} // Internal temp pool, only for corresponding P, no lock needed
shared []interface{} // Public pool accessible by other Ps (protected by lock)
Mutex // Lock protecting P's public pool access
}
// Map by P ID so different Ps can steal from other pools
var (
allPoolsMu Mutex
allPools []*Pool // References to all public pools reachable by all Ps
)Put
When placing memory, it goes to private storage first. If private space is full and contents haven’t been taken yet, new memory goes into the public pool.
Memory Stealing
When the current P has no extra memory, we first acquire the mutex lock for another P’s public pool, then take directly from that pool.
Garbage Collection
func poolCleanup() {
// Still cleans up all pool memory during runtime STW
for i, p := range allPools {
allPools[i] = nil
for i := 0; i < int(p.localSize); i++ {
l := indexLocal(p.local, i)
l.private = nil
for j := range l.shared {
l.shared[j] = nil
}
l.shared = nil
}
p.local = nil
p.localSize = 0
}
allPools = []*Pool{}
}
// Registers this function with runtime on import
func init() {
runtime_registerPoolCleanup(poolCleanup)
}Concurrency Safety Through Pin Operations
The implementation achieves thread safety by preventing P preemption and using mutex locks between multiple Ps.
func runtime_procPin() int
func runtime_procUnpin()