sync.Pool Under the Hood

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()