mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 22:52:25 -06:00 
			
		
		
		
	
		
			
	
	
		
			388 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			388 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package pools
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"runtime"
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
									"sync/atomic"
							 | 
						||
| 
								 | 
							
									"unsafe"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type Pool struct {
							 | 
						||
| 
								 | 
							
									// New is used to instantiate new items
							 | 
						||
| 
								 | 
							
									New func() interface{}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Evict is called on evicted items during pool .Clean()
							 | 
						||
| 
								 | 
							
									Evict func(interface{})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									local    unsafe.Pointer // ptr to []_ppool
							 | 
						||
| 
								 | 
							
									localSz  int64          // count of all elems in local
							 | 
						||
| 
								 | 
							
									victim   unsafe.Pointer // ptr to []_ppool
							 | 
						||
| 
								 | 
							
									victimSz int64          // count of all elems in victim
							 | 
						||
| 
								 | 
							
									mutex    sync.Mutex     // mutex protects new cleanups, and new allocations of local
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Get attempts to fetch an item from the pool, failing that allocates with supplied .New() function
							 | 
						||
| 
								 | 
							
								func (p *Pool) Get() interface{} {
							 | 
						||
| 
								 | 
							
									// Get local pool for proc
							 | 
						||
| 
								 | 
							
									// (also pins proc)
							 | 
						||
| 
								 | 
							
									pool, pid := p.pin()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if v := pool.getPrivate(); v != nil {
							 | 
						||
| 
								 | 
							
										// local _ppool private elem acquired
							 | 
						||
| 
								 | 
							
										runtime_procUnpin()
							 | 
						||
| 
								 | 
							
										atomic.AddInt64(&p.localSz, -1)
							 | 
						||
| 
								 | 
							
										return v
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if v := pool.get(); v != nil {
							 | 
						||
| 
								 | 
							
										// local _ppool queue elem acquired
							 | 
						||
| 
								 | 
							
										runtime_procUnpin()
							 | 
						||
| 
								 | 
							
										atomic.AddInt64(&p.localSz, -1)
							 | 
						||
| 
								 | 
							
										return v
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Unpin before attempting slow
							 | 
						||
| 
								 | 
							
									runtime_procUnpin()
							 | 
						||
| 
								 | 
							
									if v := p.getSlow(pid); v != nil {
							 | 
						||
| 
								 | 
							
										// note size decrementing
							 | 
						||
| 
								 | 
							
										// is handled within p.getSlow()
							 | 
						||
| 
								 | 
							
										// as we don't know if it came
							 | 
						||
| 
								 | 
							
										// from the local or victim pools
							 | 
						||
| 
								 | 
							
										return v
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Alloc new
							 | 
						||
| 
								 | 
							
									return p.New()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Put places supplied item in the proc local pool
							 | 
						||
| 
								 | 
							
								func (p *Pool) Put(v interface{}) {
							 | 
						||
| 
								 | 
							
									// Don't store nil
							 | 
						||
| 
								 | 
							
									if v == nil {
							 | 
						||
| 
								 | 
							
										return
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Get proc local pool
							 | 
						||
| 
								 | 
							
									// (also pins proc)
							 | 
						||
| 
								 | 
							
									pool, _ := p.pin()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// first try private, then queue
							 | 
						||
| 
								 | 
							
									if !pool.setPrivate(v) {
							 | 
						||
| 
								 | 
							
										pool.put(v)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									runtime_procUnpin()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Increment local pool size
							 | 
						||
| 
								 | 
							
									atomic.AddInt64(&p.localSz, 1)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Clean will drop the current victim pools, move the current local pools to its
							 | 
						||
| 
								 | 
							
								// place and reset the local pools ptr in order to be regenerated
							 | 
						||
| 
								 | 
							
								func (p *Pool) Clean() {
							 | 
						||
| 
								 | 
							
									p.mutex.Lock()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// victim becomes local, local becomes nil
							 | 
						||
| 
								 | 
							
									localPtr := atomic.SwapPointer(&p.local, nil)
							 | 
						||
| 
								 | 
							
									victimPtr := atomic.SwapPointer(&p.victim, localPtr)
							 | 
						||
| 
								 | 
							
									localSz := atomic.SwapInt64(&p.localSz, 0)
							 | 
						||
| 
								 | 
							
									atomic.StoreInt64(&p.victimSz, localSz)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var victim []ppool
							 | 
						||
| 
								 | 
							
									if victimPtr != nil {
							 | 
						||
| 
								 | 
							
										victim = *(*[]ppool)(victimPtr)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// drain each of the vict _ppool items
							 | 
						||
| 
								 | 
							
									for i := 0; i < len(victim); i++ {
							 | 
						||
| 
								 | 
							
										ppool := &victim[i]
							 | 
						||
| 
								 | 
							
										ppool.evict(p.Evict)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									p.mutex.Unlock()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// LocalSize returns the total number of elements in all the proc-local pools
							 | 
						||
| 
								 | 
							
								func (p *Pool) LocalSize() int64 {
							 | 
						||
| 
								 | 
							
									return atomic.LoadInt64(&p.localSz)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// VictimSize returns the total number of elements in all the victim (old proc-local) pools
							 | 
						||
| 
								 | 
							
								func (p *Pool) VictimSize() int64 {
							 | 
						||
| 
								 | 
							
									return atomic.LoadInt64(&p.victimSz)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// getSlow is the slow path for fetching an element, attempting to steal from other proc's
							 | 
						||
| 
								 | 
							
								// local pools, and failing that, from the aging-out victim pools. pid is still passed so
							 | 
						||
| 
								 | 
							
								// not all procs start iterating from the same index
							 | 
						||
| 
								 | 
							
								func (p *Pool) getSlow(pid int) interface{} {
							 | 
						||
| 
								 | 
							
									// get local pools
							 | 
						||
| 
								 | 
							
									local := p.localPools()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Try to steal from other proc locals
							 | 
						||
| 
								 | 
							
									for i := 0; i < len(local); i++ {
							 | 
						||
| 
								 | 
							
										pool := &local[(pid+i+1)%len(local)]
							 | 
						||
| 
								 | 
							
										if v := pool.get(); v != nil {
							 | 
						||
| 
								 | 
							
											atomic.AddInt64(&p.localSz, -1)
							 | 
						||
| 
								 | 
							
											return v
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// get victim pools
							 | 
						||
| 
								 | 
							
									victim := p.victimPools()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Attempt to steal from victim pools
							 | 
						||
| 
								 | 
							
									for i := 0; i < len(victim); i++ {
							 | 
						||
| 
								 | 
							
										pool := &victim[(pid+i+1)%len(victim)]
							 | 
						||
| 
								 | 
							
										if v := pool.get(); v != nil {
							 | 
						||
| 
								 | 
							
											atomic.AddInt64(&p.victimSz, -1)
							 | 
						||
| 
								 | 
							
											return v
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Set victim pools to nil (none found)
							 | 
						||
| 
								 | 
							
									atomic.StorePointer(&p.victim, nil)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// localPools safely loads slice of local _ppools
							 | 
						||
| 
								 | 
							
								func (p *Pool) localPools() []ppool {
							 | 
						||
| 
								 | 
							
									local := atomic.LoadPointer(&p.local)
							 | 
						||
| 
								 | 
							
									if local == nil {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return *(*[]ppool)(local)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// victimPools safely loads slice of victim _ppools
							 | 
						||
| 
								 | 
							
								func (p *Pool) victimPools() []ppool {
							 | 
						||
| 
								 | 
							
									victim := atomic.LoadPointer(&p.victim)
							 | 
						||
| 
								 | 
							
									if victim == nil {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return *(*[]ppool)(victim)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// pin will get fetch pin proc to PID, fetch proc-local _ppool and current PID we're pinned to
							 | 
						||
| 
								 | 
							
								func (p *Pool) pin() (*ppool, int) {
							 | 
						||
| 
								 | 
							
									for {
							 | 
						||
| 
								 | 
							
										// get local pools
							 | 
						||
| 
								 | 
							
										local := p.localPools()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if len(local) > 0 {
							 | 
						||
| 
								 | 
							
											// local already initialized
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// pin to current proc
							 | 
						||
| 
								 | 
							
											pid := runtime_procPin()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// check for pid local pool
							 | 
						||
| 
								 | 
							
											if pid < len(local) {
							 | 
						||
| 
								 | 
							
												return &local[pid], pid
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// unpin from proc
							 | 
						||
| 
								 | 
							
											runtime_procUnpin()
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											// local not yet initialized
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Check functions are set
							 | 
						||
| 
								 | 
							
											if p.New == nil {
							 | 
						||
| 
								 | 
							
												panic("new func must not be nil")
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if p.Evict == nil {
							 | 
						||
| 
								 | 
							
												panic("evict func must not be nil")
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// allocate local
							 | 
						||
| 
								 | 
							
										p.allocLocal()
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// allocLocal allocates a new local pool slice, with the old length passed to check
							 | 
						||
| 
								 | 
							
								// if pool was previously nil, or whether a change in GOMAXPROCS occurred
							 | 
						||
| 
								 | 
							
								func (p *Pool) allocLocal() {
							 | 
						||
| 
								 | 
							
									// get pool lock
							 | 
						||
| 
								 | 
							
									p.mutex.Lock()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Calculate new size to use
							 | 
						||
| 
								 | 
							
									size := runtime.GOMAXPROCS(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									local := p.localPools()
							 | 
						||
| 
								 | 
							
									if len(local) != size {
							 | 
						||
| 
								 | 
							
										// GOMAXPROCS changed, reallocate
							 | 
						||
| 
								 | 
							
										pools := make([]ppool, size)
							 | 
						||
| 
								 | 
							
										atomic.StorePointer(&p.local, unsafe.Pointer(&pools))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Evict old local elements
							 | 
						||
| 
								 | 
							
										for i := 0; i < len(local); i++ {
							 | 
						||
| 
								 | 
							
											pool := &local[i]
							 | 
						||
| 
								 | 
							
											pool.evict(p.Evict)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Unlock pool
							 | 
						||
| 
								 | 
							
									p.mutex.Unlock()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// _ppool is a proc local pool
							 | 
						||
| 
								 | 
							
								type _ppool struct {
							 | 
						||
| 
								 | 
							
									// root is the root element of the _ppool queue,
							 | 
						||
| 
								 | 
							
									// and protects concurrent access to the queue
							 | 
						||
| 
								 | 
							
									root unsafe.Pointer
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// private is a proc private member accessible
							 | 
						||
| 
								 | 
							
									// only to the pid this _ppool is assigned to,
							 | 
						||
| 
								 | 
							
									// except during evict (hence the unsafe pointer)
							 | 
						||
| 
								 | 
							
									private unsafe.Pointer
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ppool wraps _ppool with pad.
							 | 
						||
| 
								 | 
							
								type ppool struct {
							 | 
						||
| 
								 | 
							
									_ppool
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Prevents false sharing on widespread platforms with
							 | 
						||
| 
								 | 
							
									// 128 mod (cache line size) = 0 .
							 | 
						||
| 
								 | 
							
									pad [128 - unsafe.Sizeof(_ppool{})%128]byte
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// getPrivate gets the proc private member
							 | 
						||
| 
								 | 
							
								func (pp *_ppool) getPrivate() interface{} {
							 | 
						||
| 
								 | 
							
									ptr := atomic.SwapPointer(&pp.private, nil)
							 | 
						||
| 
								 | 
							
									if ptr == nil {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return *(*interface{})(ptr)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// setPrivate sets the proc private member (only if unset)
							 | 
						||
| 
								 | 
							
								func (pp *_ppool) setPrivate(v interface{}) bool {
							 | 
						||
| 
								 | 
							
									return atomic.CompareAndSwapPointer(&pp.private, nil, unsafe.Pointer(&v))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// get fetches an element from the queue
							 | 
						||
| 
								 | 
							
								func (pp *_ppool) get() interface{} {
							 | 
						||
| 
								 | 
							
									for {
							 | 
						||
| 
								 | 
							
										// Attempt to load root elem
							 | 
						||
| 
								 | 
							
										root := atomic.LoadPointer(&pp.root)
							 | 
						||
| 
								 | 
							
										if root == nil {
							 | 
						||
| 
								 | 
							
											return nil
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Attempt to consume root elem
							 | 
						||
| 
								 | 
							
										if root == inUsePtr ||
							 | 
						||
| 
								 | 
							
											!atomic.CompareAndSwapPointer(&pp.root, root, inUsePtr) {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Root becomes next in chain
							 | 
						||
| 
								 | 
							
										e := (*elem)(root)
							 | 
						||
| 
								 | 
							
										v := e.value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Place new root back in the chain
							 | 
						||
| 
								 | 
							
										atomic.StorePointer(&pp.root, unsafe.Pointer(e.next))
							 | 
						||
| 
								 | 
							
										putElem(e)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return v
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// put places an element in the queue
							 | 
						||
| 
								 | 
							
								func (pp *_ppool) put(v interface{}) {
							 | 
						||
| 
								 | 
							
									// Prepare next elem
							 | 
						||
| 
								 | 
							
									e := getElem()
							 | 
						||
| 
								 | 
							
									e.value = v
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for {
							 | 
						||
| 
								 | 
							
										// Attempt to load root elem
							 | 
						||
| 
								 | 
							
										root := atomic.LoadPointer(&pp.root)
							 | 
						||
| 
								 | 
							
										if root == inUsePtr {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Set the next elem value (might be nil)
							 | 
						||
| 
								 | 
							
										e.next = (*elem)(root)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Attempt to store this new value at root
							 | 
						||
| 
								 | 
							
										if atomic.CompareAndSwapPointer(&pp.root, root, unsafe.Pointer(e)) {
							 | 
						||
| 
								 | 
							
											break
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// hook evicts all entries from pool, calling hook on each
							 | 
						||
| 
								 | 
							
								func (pp *_ppool) evict(hook func(interface{})) {
							 | 
						||
| 
								 | 
							
									if v := pp.getPrivate(); v != nil {
							 | 
						||
| 
								 | 
							
										hook(v)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									for {
							 | 
						||
| 
								 | 
							
										v := pp.get()
							 | 
						||
| 
								 | 
							
										if v == nil {
							 | 
						||
| 
								 | 
							
											break
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										hook(v)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// inUsePtr is a ptr used to indicate _ppool is in use
							 | 
						||
| 
								 | 
							
								var inUsePtr = unsafe.Pointer(&elem{
							 | 
						||
| 
								 | 
							
									next:  nil,
							 | 
						||
| 
								 | 
							
									value: "in_use",
							 | 
						||
| 
								 | 
							
								})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// elem defines an element in the _ppool queue
							 | 
						||
| 
								 | 
							
								type elem struct {
							 | 
						||
| 
								 | 
							
									next  *elem
							 | 
						||
| 
								 | 
							
									value interface{}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// elemPool is a simple pool of unused elements
							 | 
						||
| 
								 | 
							
								var elemPool = struct {
							 | 
						||
| 
								 | 
							
									root unsafe.Pointer
							 | 
						||
| 
								 | 
							
								}{}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// getElem fetches a new elem from pool, or creates new
							 | 
						||
| 
								 | 
							
								func getElem() *elem {
							 | 
						||
| 
								 | 
							
									// Attempt to load root elem
							 | 
						||
| 
								 | 
							
									root := atomic.LoadPointer(&elemPool.root)
							 | 
						||
| 
								 | 
							
									if root == nil {
							 | 
						||
| 
								 | 
							
										return &elem{}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Attempt to consume root elem
							 | 
						||
| 
								 | 
							
									if root == inUsePtr ||
							 | 
						||
| 
								 | 
							
										!atomic.CompareAndSwapPointer(&elemPool.root, root, inUsePtr) {
							 | 
						||
| 
								 | 
							
										return &elem{}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Root becomes next in chain
							 | 
						||
| 
								 | 
							
									e := (*elem)(root)
							 | 
						||
| 
								 | 
							
									atomic.StorePointer(&elemPool.root, unsafe.Pointer(e.next))
							 | 
						||
| 
								 | 
							
									e.next = nil
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return e
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// putElem will place element in the pool
							 | 
						||
| 
								 | 
							
								func putElem(e *elem) {
							 | 
						||
| 
								 | 
							
									e.value = nil
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Attempt to load root elem
							 | 
						||
| 
								 | 
							
									root := atomic.LoadPointer(&elemPool.root)
							 | 
						||
| 
								 | 
							
									if root == inUsePtr {
							 | 
						||
| 
								 | 
							
										return // drop
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Set the next elem value (might be nil)
							 | 
						||
| 
								 | 
							
									e.next = (*elem)(root)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Attempt to store this new value at root
							 | 
						||
| 
								 | 
							
									atomic.CompareAndSwapPointer(&elemPool.root, root, unsafe.Pointer(e))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//go:linkname runtime_procPin sync.runtime_procPin
							 | 
						||
| 
								 | 
							
								func runtime_procPin() int
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//go:linkname runtime_procUnpin sync.runtime_procUnpin
							 | 
						||
| 
								 | 
							
								func runtime_procUnpin()
							 |