...
Run Format

Source file src/sync/pool.go

     1	// Copyright 2013 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package sync
     6	
     7	import (
     8		"internal/race"
     9		"runtime"
    10		"sync/atomic"
    11		"unsafe"
    12	)
    13	
    14	// A Pool is a set of temporary objects that may be individually saved and
    15	// retrieved.
    16	//
    17	// Any item stored in the Pool may be removed automatically at any time without
    18	// notification. If the Pool holds the only reference when this happens, the
    19	// item might be deallocated.
    20	//
    21	// A Pool is safe for use by multiple goroutines simultaneously.
    22	//
    23	// Pool's purpose is to cache allocated but unused items for later reuse,
    24	// relieving pressure on the garbage collector. That is, it makes it easy to
    25	// build efficient, thread-safe free lists. However, it is not suitable for all
    26	// free lists.
    27	//
    28	// An appropriate use of a Pool is to manage a group of temporary items
    29	// silently shared among and potentially reused by concurrent independent
    30	// clients of a package. Pool provides a way to amortize allocation overhead
    31	// across many clients.
    32	//
    33	// An example of good use of a Pool is in the fmt package, which maintains a
    34	// dynamically-sized store of temporary output buffers. The store scales under
    35	// load (when many goroutines are actively printing) and shrinks when
    36	// quiescent.
    37	//
    38	// On the other hand, a free list maintained as part of a short-lived object is
    39	// not a suitable use for a Pool, since the overhead does not amortize well in
    40	// that scenario. It is more efficient to have such objects implement their own
    41	// free list.
    42	//
    43	// A Pool must not be copied after first use.
    44	type Pool struct {
    45		noCopy noCopy
    46	
    47		local     unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal
    48		localSize uintptr        // size of the local array
    49	
    50		// New optionally specifies a function to generate
    51		// a value when Get would otherwise return nil.
    52		// It may not be changed concurrently with calls to Get.
    53		New func() interface{}
    54	}
    55	
    56	// Local per-P Pool appendix.
    57	type poolLocal struct {
    58		private interface{}   // Can be used only by the respective P.
    59		shared  []interface{} // Can be used by any P.
    60		Mutex                 // Protects shared.
    61		pad     [128]byte     // Prevents false sharing.
    62	}
    63	
    64	// Put adds x to the pool.
    65	func (p *Pool) Put(x interface{}) {
    66		if race.Enabled {
    67			// Under race detector the Pool degenerates into no-op.
    68			// It's conforming, simple and does not introduce excessive
    69			// happens-before edges between unrelated goroutines.
    70			return
    71		}
    72		if x == nil {
    73			return
    74		}
    75		l := p.pin()
    76		if l.private == nil {
    77			l.private = x
    78			x = nil
    79		}
    80		runtime_procUnpin()
    81		if x == nil {
    82			return
    83		}
    84		l.Lock()
    85		l.shared = append(l.shared, x)
    86		l.Unlock()
    87	}
    88	
    89	// Get selects an arbitrary item from the Pool, removes it from the
    90	// Pool, and returns it to the caller.
    91	// Get may choose to ignore the pool and treat it as empty.
    92	// Callers should not assume any relation between values passed to Put and
    93	// the values returned by Get.
    94	//
    95	// If Get would otherwise return nil and p.New is non-nil, Get returns
    96	// the result of calling p.New.
    97	func (p *Pool) Get() interface{} {
    98		if race.Enabled {
    99			if p.New != nil {
   100				return p.New()
   101			}
   102			return nil
   103		}
   104		l := p.pin()
   105		x := l.private
   106		l.private = nil
   107		runtime_procUnpin()
   108		if x != nil {
   109			return x
   110		}
   111		l.Lock()
   112		last := len(l.shared) - 1
   113		if last >= 0 {
   114			x = l.shared[last]
   115			l.shared = l.shared[:last]
   116		}
   117		l.Unlock()
   118		if x != nil {
   119			return x
   120		}
   121		return p.getSlow()
   122	}
   123	
   124	func (p *Pool) getSlow() (x interface{}) {
   125		// See the comment in pin regarding ordering of the loads.
   126		size := atomic.LoadUintptr(&p.localSize) // load-acquire
   127		local := p.local                         // load-consume
   128		// Try to steal one element from other procs.
   129		pid := runtime_procPin()
   130		runtime_procUnpin()
   131		for i := 0; i < int(size); i++ {
   132			l := indexLocal(local, (pid+i+1)%int(size))
   133			l.Lock()
   134			last := len(l.shared) - 1
   135			if last >= 0 {
   136				x = l.shared[last]
   137				l.shared = l.shared[:last]
   138				l.Unlock()
   139				break
   140			}
   141			l.Unlock()
   142		}
   143	
   144		if x == nil && p.New != nil {
   145			x = p.New()
   146		}
   147		return x
   148	}
   149	
   150	// pin pins the current goroutine to P, disables preemption and returns poolLocal pool for the P.
   151	// Caller must call runtime_procUnpin() when done with the pool.
   152	func (p *Pool) pin() *poolLocal {
   153		pid := runtime_procPin()
   154		// In pinSlow we store to localSize and then to local, here we load in opposite order.
   155		// Since we've disabled preemption, GC cannot happen in between.
   156		// Thus here we must observe local at least as large localSize.
   157		// We can observe a newer/larger local, it is fine (we must observe its zero-initialized-ness).
   158		s := atomic.LoadUintptr(&p.localSize) // load-acquire
   159		l := p.local                          // load-consume
   160		if uintptr(pid) < s {
   161			return indexLocal(l, pid)
   162		}
   163		return p.pinSlow()
   164	}
   165	
   166	func (p *Pool) pinSlow() *poolLocal {
   167		// Retry under the mutex.
   168		// Can not lock the mutex while pinned.
   169		runtime_procUnpin()
   170		allPoolsMu.Lock()
   171		defer allPoolsMu.Unlock()
   172		pid := runtime_procPin()
   173		// poolCleanup won't be called while we are pinned.
   174		s := p.localSize
   175		l := p.local
   176		if uintptr(pid) < s {
   177			return indexLocal(l, pid)
   178		}
   179		if p.local == nil {
   180			allPools = append(allPools, p)
   181		}
   182		// If GOMAXPROCS changes between GCs, we re-allocate the array and lose the old one.
   183		size := runtime.GOMAXPROCS(0)
   184		local := make([]poolLocal, size)
   185		atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-release
   186		atomic.StoreUintptr(&p.localSize, uintptr(size))         // store-release
   187		return &local[pid]
   188	}
   189	
   190	func poolCleanup() {
   191		// This function is called with the world stopped, at the beginning of a garbage collection.
   192		// It must not allocate and probably should not call any runtime functions.
   193		// Defensively zero out everything, 2 reasons:
   194		// 1. To prevent false retention of whole Pools.
   195		// 2. If GC happens while a goroutine works with l.shared in Put/Get,
   196		//    it will retain whole Pool. So next cycle memory consumption would be doubled.
   197		for i, p := range allPools {
   198			allPools[i] = nil
   199			for i := 0; i < int(p.localSize); i++ {
   200				l := indexLocal(p.local, i)
   201				l.private = nil
   202				for j := range l.shared {
   203					l.shared[j] = nil
   204				}
   205				l.shared = nil
   206			}
   207			p.local = nil
   208			p.localSize = 0
   209		}
   210		allPools = []*Pool{}
   211	}
   212	
   213	var (
   214		allPoolsMu Mutex
   215		allPools   []*Pool
   216	)
   217	
   218	func init() {
   219		runtime_registerPoolCleanup(poolCleanup)
   220	}
   221	
   222	func indexLocal(l unsafe.Pointer, i int) *poolLocal {
   223		return &(*[1000000]poolLocal)(l)[i]
   224	}
   225	
   226	// Implemented in runtime.
   227	func runtime_registerPoolCleanup(cleanup func())
   228	func runtime_procPin() int
   229	func runtime_procUnpin()
   230	

View as plain text