github.com/Cloud-Foundations/Dominator@v0.3.4/lib/resourcepool/api.go (about)

     1  /*
     2  	Package resourcepool provides for managing shared resource pools.
     3  
     4  	Package resourcepool may be used by other packages to manage shared
     5  	resources (e.g. the lib/connpool package uses this package to help manage
     6  	connection pools).
     7  	A typical programming pattern is:
     8  		pool := New(...)
     9  		cr0 := pool.Create(...)
    10  		cr1 := pool.Create(...)
    11  		go func() {
    12  			for ... {
    13  				c := cr0.Get(...)
    14  				defer c.Put()
    15  				if err { c.Release() }
    16  			}
    17  		}()
    18  		go func() {
    19  			for ... {
    20  				c := cr1.Get(...)
    21  				defer c.Put()
    22  				if err { c.Release() }
    23  			}
    24  		}()
    25  	This pattern ensures Get and Put are always matched, and if there is an
    26  	error, Release releases the underlying resource so that a subsequent Get
    27  	creates a new underlying resource.
    28  
    29  	It is resonable to create one goroutine for each resource, since the Get
    30  	methods will block, waiting for available resources.
    31  */
    32  package resourcepool
    33  
    34  import (
    35  	"errors"
    36  	"sync"
    37  )
    38  
    39  var (
    40  	ErrorPutTimeout            = errors.New("timed out waiting for put")
    41  	ErrorResourceLimitExceeded = errors.New("resource limit exceeded")
    42  )
    43  
    44  // MakeImmediateCanceler returns a channel that may be passed to the
    45  // Resource.Get method for callers that do not want to wait for a resource.
    46  func MakeImmediateCanceler() <-chan struct{} {
    47  	ch := make(chan struct{}, 1)
    48  	ch <- struct{}{}
    49  	return ch
    50  }
    51  
    52  // AllocateReleaser defines a type that can be used to allocate and release
    53  // resources.
    54  type AllocateReleaser interface {
    55  	Allocate() error
    56  	Release() error
    57  }
    58  
    59  // Pool groups and manages a set of resources.
    60  type Pool struct {
    61  	max          uint
    62  	semaphore    chan struct{}
    63  	lock         sync.Mutex
    64  	numUsed      uint
    65  	unused       map[*Resource]struct{}
    66  	numUnused    uint // For metrics.
    67  	numReleasing uint
    68  }
    69  
    70  // Resource is a container for an underlying resource.
    71  type Resource struct {
    72  	pool             *Pool
    73  	allocateReleaser AllocateReleaser
    74  	allocating       bool
    75  	semaphore        chan struct{}
    76  	releasing        sync.Mutex
    77  	releaseOnPut     bool
    78  	allocated        bool
    79  	releaseError     error
    80  }
    81  
    82  // New returns a new resource Pool. The maximum number of resources that can be
    83  // allocated concurrently is specified by max.
    84  func New(max uint, metricsSubDirname string) *Pool {
    85  	return newPool(max, metricsSubDirname)
    86  }
    87  
    88  // Create returns a new Resource for the pool. An unlimted number of resources
    89  // may be created. The mechanism to specify how to allocate and release
    90  // underlying resources is given by allocateReleaser. Create does not allocate
    91  // resources.
    92  func (pool *Pool) Create(allocateReleaser AllocateReleaser) *Resource {
    93  	return pool.create(allocateReleaser)
    94  }
    95  
    96  // Get attempts to allocate an underlying resource. It calls the Allocate method
    97  // of the AllocateReleaser passed to the Create method. Get will wait until a
    98  // resource is available or a message is received on cancelChannel. If
    99  // cancelChannel is nil then Get will wait indefinitely until a resource is
   100  // available. If the wait is cancelled then Get will return
   101  // ErrorResourceLimitExceeded. The resource is considered in use until a later
   102  // call to Put or Release.
   103  func (resource *Resource) Get(cancelChannel <-chan struct{}) error {
   104  	return resource.get(cancelChannel)
   105  }
   106  
   107  // Put will free the resource, indicating that it is not currently needed. It
   108  // may be internally released later if required to free limited resources. If
   109  // Put is called after Release, no action is taken (this is a safe operation and
   110  // is commonly used in some programming patterns).
   111  func (resource *Resource) Put() {
   112  	resource.put()
   113  }
   114  
   115  // Release will release the resource, immediately calling the Release method of
   116  // the AllocateReleaser and returning its return value.
   117  func (resource *Resource) Release() error {
   118  	return resource.release(false)
   119  }
   120  
   121  // ScheduleRelease will immediately release the resource if it is not currently
   122  // in use, otherwise it will schedule the resource to be released after the next
   123  // Put.
   124  func (resource *Resource) ScheduleRelease() error {
   125  	return resource.scheduleRelease()
   126  }