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 }