github.com/Cloud-Foundations/Dominator@v0.3.4/lib/resourcepool/impl.go (about) 1 package resourcepool 2 3 func newPool(max uint, metricsSubDirname string) *Pool { 4 pool := &Pool{ 5 max: max, 6 semaphore: make(chan struct{}, max), 7 unused: make(map[*Resource]struct{}), 8 } 9 pool.registerMetrics(metricsSubDirname) 10 return pool 11 } 12 13 func (pool *Pool) create(allocateReleaser AllocateReleaser) *Resource { 14 return &Resource{ 15 pool: pool, 16 allocateReleaser: allocateReleaser, 17 semaphore: make(chan struct{}, 1)} 18 } 19 20 func (pool *Pool) getSlot(cancelChannel <-chan struct{}) bool { 21 // Grab a slot (the right to have a resource in use). 22 select { 23 case pool.semaphore <- struct{}{}: 24 return true 25 default: 26 } 27 select { 28 case pool.semaphore <- struct{}{}: 29 return true 30 case <-cancelChannel: 31 return false 32 } 33 } 34 35 func (resource *Resource) get(cancelChannel <-chan struct{}) error { 36 drainSemaphore := false 37 defer func() { 38 if drainSemaphore { 39 <-resource.semaphore 40 } 41 }() 42 select { 43 case resource.semaphore <- struct{}{}: 44 drainSemaphore = true 45 default: 46 select { 47 case resource.semaphore <- struct{}{}: 48 drainSemaphore = true 49 case <-cancelChannel: 50 return ErrorPutTimeout 51 } 52 } 53 pool := resource.pool 54 if !pool.getSlot(cancelChannel) { 55 return ErrorResourceLimitExceeded 56 } 57 pool.lock.Lock() 58 defer pool.lock.Unlock() 59 if resource.allocated { 60 delete(pool.unused, resource) 61 pool.numUnused = uint(len(pool.unused)) 62 drainSemaphore = false 63 pool.numUsed++ 64 return nil 65 } 66 if pool.numUsed+uint(len(pool.unused))+pool.numReleasing >= pool.max { 67 // Need to grab a free resource and release. Be lazy: do a random pick. 68 var resourceToRelease *Resource 69 for res := range pool.unused { 70 resourceToRelease = res 71 break 72 } 73 if resourceToRelease == nil { 74 panic("No free resource to release") 75 } 76 if !resourceToRelease.allocated { 77 panic("Resource is not allocated") 78 } 79 delete(pool.unused, resourceToRelease) 80 pool.numUnused = uint(len(pool.unused)) 81 resourceToRelease.allocated = false 82 pool.numReleasing++ 83 resourceToRelease.releasing.Lock() 84 pool.lock.Unlock() 85 resourceToRelease.releaseError = 86 resourceToRelease.allocateReleaser.Release() 87 pool.lock.Lock() 88 resourceToRelease.releasing.Unlock() 89 pool.numReleasing-- 90 } 91 resource.allocating = true 92 resource.allocated = true 93 pool.numUsed++ 94 pool.lock.Unlock() 95 resource.releasing.Lock() // Wait for myself to finish releasing. 96 resource.releasing.Unlock() 97 err := resource.allocateReleaser.Allocate() 98 pool.lock.Lock() 99 resource.allocating = false 100 if err != nil { 101 resource.allocated = false 102 pool.numUsed-- 103 <-pool.semaphore // Free up a slot for someone else. 104 return err 105 } 106 drainSemaphore = false 107 return nil 108 } 109 110 func (resource *Resource) put() { 111 pool := resource.pool 112 pool.lock.Lock() 113 if !resource.allocated { 114 pool.lock.Unlock() 115 return 116 } 117 if len(resource.semaphore) < 1 { 118 pool.lock.Unlock() 119 panic("Resource was not gotten") 120 } 121 if resource.releaseOnPut { 122 resource.release(true) 123 return 124 } 125 pool.unused[resource] = struct{}{} 126 pool.numUnused = uint(len(pool.unused)) 127 <-resource.semaphore 128 pool.numUsed-- 129 pool.lock.Unlock() 130 <-pool.semaphore // Free up a slot for someone else. 131 } 132 133 func (resource *Resource) release(haveLock bool) error { 134 pool := resource.pool 135 if !haveLock { 136 pool.lock.Lock() 137 } 138 if resource.allocating { 139 pool.lock.Unlock() 140 panic("Resource is allocating") 141 } 142 if !resource.allocated { 143 pool.lock.Unlock() 144 return resource.releaseError 145 } 146 delete(resource.pool.unused, resource) 147 pool.numUnused = uint(len(pool.unused)) 148 resource.allocated = false 149 wasUsed := false 150 if len(resource.semaphore) > 0 { 151 wasUsed = true 152 <-resource.semaphore 153 pool.numUsed-- 154 } 155 pool.numReleasing++ 156 pool.lock.Unlock() 157 resource.releaseError = resource.allocateReleaser.Release() 158 pool.lock.Lock() 159 pool.numReleasing-- 160 pool.lock.Unlock() 161 if wasUsed { 162 <-pool.semaphore // Free up a slot for someone else. 163 } 164 return resource.releaseError 165 } 166 167 func (resource *Resource) scheduleRelease() error { 168 resource.pool.lock.Lock() 169 if len(resource.semaphore) > 0 { 170 resource.releaseOnPut = true 171 resource.pool.lock.Unlock() 172 return nil 173 } 174 return resource.release(true) 175 }