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  }