github.com/Cloud-Foundations/Dominator@v0.3.4/lib/resourcepool/locking_test.go (about) 1 package resourcepool 2 3 import ( 4 "sync" 5 "testing" 6 ) 7 8 type testPoolType struct { 9 sync.Mutex 10 pool *Pool 11 max int 12 resources []*testResourceType 13 numActive int 14 maxNumActive int 15 numInUse int 16 maxNumInUse int 17 } 18 19 func newTestPool(max uint, numResources uint) *testPoolType { 20 testPool := &testPoolType{ 21 pool: New(max, ""), 22 max: int(max), 23 resources: make([]*testResourceType, 0, numResources), 24 } 25 for count := 0; count < int(numResources); count++ { 26 testResource := &testResourceType{testPool: testPool} 27 testResource.resource = testPool.pool.Create(testResource) 28 testPool.resources = append(testPool.resources, testResource) 29 } 30 return testPool 31 } 32 33 func (testPool *testPoolType) getNumActive() int { 34 testPool.Lock() 35 defer testPool.Unlock() 36 return testPool.numActive 37 } 38 39 func (testPool *testPoolType) getMaxNumActive() int { 40 testPool.Lock() 41 defer testPool.Unlock() 42 return testPool.maxNumActive 43 } 44 45 func (testPool *testPoolType) getNumInUse() int { 46 testPool.Lock() 47 defer testPool.Unlock() 48 return testPool.numInUse 49 } 50 51 func (testPool *testPoolType) getMaxNumInUse() int { 52 testPool.Lock() 53 defer testPool.Unlock() 54 return testPool.maxNumInUse 55 } 56 57 type testResourceType struct { 58 testPool *testPoolType 59 resource *Resource 60 active bool 61 } 62 63 func (testResource *testResourceType) Allocate() error { 64 testPool := testResource.testPool 65 if testResource.active { 66 panic("resource already allocated") 67 } 68 testPool.Lock() 69 defer testPool.Unlock() 70 if testPool.numActive >= testPool.max { 71 panic("Capacity exceeded") 72 } 73 testPool.numActive++ 74 if testPool.numActive > testPool.maxNumActive { 75 testPool.maxNumActive = testPool.numActive 76 } 77 testResource.active = true 78 return nil 79 } 80 81 func (testResource *testResourceType) get(cancelChannel <-chan struct{}) error { 82 if err := testResource.resource.Get(cancelChannel); err != nil { 83 return err 84 } 85 testPool := testResource.testPool 86 testPool.Lock() 87 defer testPool.Unlock() 88 if testPool.numInUse >= testPool.max { 89 panic("numInUse exceeding capacity") 90 } 91 testPool.numInUse++ 92 if testPool.numInUse > testPool.maxNumInUse { 93 testPool.maxNumInUse = testPool.numInUse 94 } 95 return nil 96 } 97 98 func (testResource *testResourceType) put() { 99 testPool := testResource.testPool 100 testPool.Lock() 101 if testResource.active { 102 testPool.numInUse-- 103 } 104 testPool.Unlock() 105 testResource.resource.Put() 106 } 107 108 func (testResource *testResourceType) release() error { 109 testPool := testResource.testPool 110 testPool.Lock() 111 if testResource.active { 112 testPool.numInUse-- 113 } 114 testPool.Unlock() 115 return testResource.resource.Release() 116 } 117 118 func (testResource *testResourceType) Release() error { 119 testPool := testResource.testPool 120 testPool.Lock() 121 defer testPool.Unlock() 122 testPool.numActive-- 123 if testResource.active { 124 testResource.active = false 125 } else { 126 panic("Resource re-released") 127 } 128 return nil 129 } 130 131 func TestGetPut(t *testing.T) { 132 testPool := newTestPool(1, 1) 133 testResource := testPool.resources[0] 134 if testResource.get(MakeImmediateCanceler()) != nil { 135 t.Errorf("Get(): would have waited") 136 } 137 tmp := testPool.getNumInUse() 138 if tmp != 1 { 139 t.Errorf("numInUse = %v", tmp) 140 } 141 if !testResource.active { 142 t.Errorf("Resource should not have been released") 143 } 144 testResource.put() 145 if !testResource.active { 146 t.Errorf("Resource should not have been released") 147 } 148 tmp = testPool.getNumInUse() 149 if tmp != 0 { 150 t.Errorf("numInUse = %v", tmp) 151 } 152 tmp = testPool.getNumActive() 153 if tmp != 1 { 154 t.Errorf("numActive = %v", tmp) 155 } 156 tmp = testPool.getMaxNumActive() 157 if tmp != 1 { 158 t.Errorf("maxNumActive = %v", tmp) 159 } 160 } 161 162 func TestGetClosePut(t *testing.T) { 163 testPool := newTestPool(1, 1) 164 testResource := testPool.resources[0] 165 if testResource.get(nil) != nil { 166 t.Errorf("Get(): would have waited") 167 } 168 tmp := testPool.getNumInUse() 169 if tmp != 1 { 170 t.Errorf("numInUse = %v", tmp) 171 } 172 if !testResource.active { 173 t.Errorf("Resource should not have been released") 174 } 175 testResource.release() 176 tmp = testPool.getNumInUse() 177 if tmp != 0 { 178 t.Errorf("numInUse = %v", tmp) 179 } 180 if testResource.active { 181 t.Errorf("Resource should have been released") 182 } 183 tmp = testPool.getNumActive() 184 if tmp != 0 { 185 t.Errorf("numActive = %v", tmp) 186 } 187 testResource.put() 188 } 189 190 func TestGetPutPut(t *testing.T) { 191 testPool := newTestPool(1, 1) 192 testResource := testPool.resources[0] 193 defer func() { 194 if err := recover(); err == nil { 195 t.Errorf("Multiple Put() did not panic") 196 } 197 }() 198 testResource.get(nil) 199 testResource.put() 200 testResource.put() 201 } 202 203 func (testPool *testPoolType) testConcurrent(t *testing.T, numCycles int, 204 testFunc func(*testResourceType, int)) { 205 finished := make(chan struct{}, len(testPool.resources)) 206 for _, resource := range testPool.resources { 207 go func(r *testResourceType) { 208 testFunc(r, numCycles) 209 finished <- struct{}{} 210 }(resource) 211 } 212 for range testPool.resources { 213 <-finished 214 } 215 tmp := testPool.getNumInUse() 216 if tmp != 0 { 217 t.Errorf("numInUse = %v", tmp) 218 } 219 tmp = testPool.getMaxNumInUse() 220 expected := testPool.max 221 if len(testPool.resources) < expected { 222 expected = len(testPool.resources) 223 } 224 if tmp > expected { 225 t.Errorf("maxNumInUse = %v", tmp) 226 } 227 tmp = testPool.getNumActive() 228 if tmp > testPool.max { 229 t.Errorf("numActive = %v", tmp) 230 } 231 tmp = testPool.getMaxNumActive() 232 if tmp > expected { 233 t.Errorf("maxNumActive = %v", tmp) 234 } 235 } 236 237 func testManyGetPut(resource *testResourceType, numCycles int) { 238 for count := 0; count < numCycles; count++ { 239 resource.get(nil) 240 resource.put() 241 } 242 } 243 244 func testManyGetClosePut(resource *testResourceType, numCycles int) { 245 for count := 0; count < numCycles; count++ { 246 resource.get(nil) 247 resource.release() 248 resource.put() 249 } 250 } 251 252 func TestLoopOne(t *testing.T) { 253 testPool := newTestPool(10, 1) 254 testManyGetPut(testPool.resources[0], 11) 255 testManyGetClosePut(testPool.resources[0], 11) 256 } 257 258 func TestOnlyGetClosePutLoopOne(t *testing.T) { 259 testPool := newTestPool(10, 1) 260 testManyGetClosePut(testPool.resources[0], 11) 261 } 262 263 func TestLoopOneUnderCapacity(t *testing.T) { 264 testPool := newTestPool(10, 9) 265 testPool.testConcurrent(t, 1001, testManyGetPut) 266 testPool.testConcurrent(t, 1001, testManyGetClosePut) 267 } 268 269 func TestOnlyGetClosePutLoopOneUnderCapacity(t *testing.T) { 270 testPool := newTestPool(10, 9) 271 testPool.testConcurrent(t, 1001, testManyGetClosePut) 272 } 273 274 func TestLoopAtCapacity(t *testing.T) { 275 testPool := newTestPool(10, 10) 276 testPool.testConcurrent(t, 1001, testManyGetPut) 277 testPool.testConcurrent(t, 1001, testManyGetClosePut) 278 } 279 280 func TestOnlyGetClosePutLoopAtCapacity(t *testing.T) { 281 testPool := newTestPool(10, 10) 282 testPool.testConcurrent(t, 1001, testManyGetClosePut) 283 } 284 285 func TestLoopOneOverCapacity(t *testing.T) { 286 testPool := newTestPool(10, 11) 287 testPool.testConcurrent(t, 1001, testManyGetPut) 288 testPool.testConcurrent(t, 1001, testManyGetClosePut) 289 } 290 291 func TestOnlyGetClosePutLoopOneOverCapacity(t *testing.T) { 292 testPool := newTestPool(10, 11) 293 testPool.testConcurrent(t, 1001, testManyGetClosePut) 294 } 295 296 func TestLoopFarOverCapacity(t *testing.T) { 297 testPool := newTestPool(10, 113) 298 testPool.testConcurrent(t, 1, testManyGetPut) 299 testPool.testConcurrent(t, 1001, testManyGetClosePut) 300 } 301 302 func TestOnlyGetClosePutLoopFarOverCapacity(t *testing.T) { 303 testPool := newTestPool(10, 113) 304 testPool.testConcurrent(t, 1001, testManyGetClosePut) 305 }