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  }