github.phpd.cn/cilium/cilium@v1.6.12/pkg/idpool/idpool.go (about)

     1  // Copyright 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package idpool
    16  
    17  import (
    18  	"math/rand"
    19  	"strconv"
    20  	"time"
    21  
    22  	"github.com/cilium/cilium/pkg/lock"
    23  )
    24  
    25  // ID is a numeric identifier
    26  type ID uint64
    27  
    28  // NoID is a special ID that represents "no ID available"
    29  const NoID ID = 0
    30  
    31  // String returns the string representation of an allocated ID
    32  func (i ID) String() string {
    33  	return strconv.FormatUint(uint64(i), 10)
    34  }
    35  
    36  // IDPool represents a pool of IDs that can be managed concurrently
    37  // via local usage and external events.
    38  //
    39  // An intermediate state (leased) is introduced to the life cycle
    40  // of an ID in the pool, in order to prevent lost updates to the
    41  // pool that can occur as a result of employing both management schemes
    42  // simultaneously.
    43  // Local usage of an ID becomes a two stage process of leasing
    44  // the ID from the pool, and later, Use()ing or Release()ing the ID on
    45  // the pool upon successful or unsuccessful usage respectively,
    46  //
    47  // The table below shows the state transitions in the ID's life cycle.
    48  // In the case of LeaseAvailableID() the ID is returned rather
    49  // than provided as an input to the operation.
    50  // All ID's begin in the available state.
    51  /*
    52  ---------------------------------------------------------------------
    53  |state\event   | LeaseAvailableID | Release | Use | Insert | Remove |
    54  ---------------------------------------------------------------------
    55  |1 available   |        2         |    *    |  *  |   *    |   3    |
    56  ---------------------------------------------------------------------
    57  |2 leased      |        **        |    1    |  3  |   *    |   3    |
    58  ---------------------------------------------------------------------
    59  |3 unavailable |        **        |    *    |  *  |   1    |   *    |
    60  ---------------------------------------------------------------------
    61  *  The event has no effect.
    62  ** This is guaranteed never to occur.
    63  */
    64  type IDPool struct {
    65  	// mutex protects all IDPool data structures
    66  	mutex lock.Mutex
    67  
    68  	// min is the lower limit when leasing IDs. The pool will never
    69  	// return an ID lesser than this value.
    70  	minID ID
    71  
    72  	// max is the upper limit when leasing IDs. The pool will never
    73  	// return an ID greater than this value.
    74  	maxID ID
    75  
    76  	// idCache is a cache of IDs backing the pool.
    77  	idCache *idCache
    78  }
    79  
    80  // NewIDPool returns a new ID pool
    81  func NewIDPool(minID ID, maxID ID) *IDPool {
    82  	p := &IDPool{
    83  		minID: minID,
    84  		maxID: maxID,
    85  	}
    86  
    87  	p.idCache = newIDCache(p.minID, p.maxID)
    88  
    89  	return p
    90  }
    91  
    92  // LeaseAvailableID returns an available ID at random from the pool.
    93  // Returns an ID or NoID if no there is no available ID in the pool.
    94  func (p *IDPool) LeaseAvailableID() ID {
    95  	p.mutex.Lock()
    96  	defer p.mutex.Unlock()
    97  
    98  	return p.idCache.leaseAvailableID()
    99  }
   100  
   101  // AllocateID returns a random available ID. Unlike LeaseAvailableID, the ID is
   102  // immediately marked for use and there is no need to call Use().
   103  func (p *IDPool) AllocateID() ID {
   104  	p.mutex.Lock()
   105  	defer p.mutex.Unlock()
   106  
   107  	return p.idCache.allocateID()
   108  }
   109  
   110  // Release returns a leased ID back to the pool.
   111  // This operation accounts for IDs that were previously leased
   112  // from the pool but were unused, e.g if allocation was unsuccessful.
   113  // Thus, it has no effect if the ID is not currently leased in the
   114  // pool, or the pool has since been refreshed.
   115  //
   116  // Returns true if the ID was returned back to the pool as
   117  // a result of this call.
   118  func (p *IDPool) Release(id ID) bool {
   119  	p.mutex.Lock()
   120  	defer p.mutex.Unlock()
   121  
   122  	return p.idCache.release(id)
   123  }
   124  
   125  // Use makes a leased ID unavailable in the pool and has no effect
   126  // otherwise. Returns true if the ID was made unavailable
   127  // as a result of this call.
   128  func (p *IDPool) Use(id ID) bool {
   129  	p.mutex.Lock()
   130  	defer p.mutex.Unlock()
   131  
   132  	return p.idCache.use(id)
   133  }
   134  
   135  // Insert makes an unavailable ID available in the pool
   136  // and has no effect otherwise. Returns true if the ID
   137  // was added back to the pool.
   138  func (p *IDPool) Insert(id ID) bool {
   139  	p.mutex.Lock()
   140  	defer p.mutex.Unlock()
   141  
   142  	return p.idCache.insert(id)
   143  }
   144  
   145  // Remove makes an ID unavailable in the pool.
   146  // Returns true if the ID was previously available in the pool.
   147  func (p *IDPool) Remove(id ID) bool {
   148  	p.mutex.Lock()
   149  	defer p.mutex.Unlock()
   150  
   151  	return p.idCache.remove(id)
   152  }
   153  
   154  type idCache struct {
   155  	// ids is a slice of IDs available in this idCache.
   156  	ids map[ID]struct{}
   157  
   158  	// leased is the set of IDs that are leased in this idCache.
   159  	leased map[ID]struct{}
   160  }
   161  
   162  func newIDCache(minID ID, maxID ID) *idCache {
   163  	n := int(maxID - minID + 1)
   164  	if n < 0 {
   165  		n = 0
   166  	}
   167  
   168  	c := &idCache{
   169  		ids:    make(map[ID]struct{}, n),
   170  		leased: make(map[ID]struct{}),
   171  	}
   172  
   173  	random := rand.New(rand.NewSource(time.Now().UnixNano()))
   174  	seq := random.Perm(n)
   175  	for i := 0; i < n; i++ {
   176  		id := ID(seq[i]) + minID
   177  		c.ids[id] = struct{}{}
   178  	}
   179  
   180  	return c
   181  }
   182  
   183  // allocateID returns a random available ID without leasing it
   184  func (c *idCache) allocateID() ID {
   185  	for id := range c.ids {
   186  		delete(c.ids, id)
   187  		return id
   188  	}
   189  
   190  	return NoID
   191  }
   192  
   193  // leaseAvailableID returns a random available ID.
   194  func (c *idCache) leaseAvailableID() ID {
   195  	id := c.allocateID()
   196  	if id == NoID {
   197  		return NoID
   198  	}
   199  
   200  	// Mark as leased
   201  	c.leased[id] = struct{}{}
   202  
   203  	return id
   204  }
   205  
   206  // release makes the ID available again if it is currently
   207  // leased and has no effect otherwise. Returns true if the
   208  // ID was made available as a result of this call.
   209  func (c *idCache) release(id ID) bool {
   210  	if _, exists := c.leased[id]; !exists {
   211  		return false
   212  	}
   213  
   214  	delete(c.leased, id)
   215  	c.insert(id)
   216  
   217  	return true
   218  }
   219  
   220  // use makes the ID unavailable if it is currently
   221  // leased and has no effect otherwise. Returns true if the
   222  // ID was made unavailable as a result of this call.
   223  func (c *idCache) use(id ID) bool {
   224  	if _, exists := c.leased[id]; !exists {
   225  		return false
   226  	}
   227  
   228  	delete(c.leased, id)
   229  	return true
   230  }
   231  
   232  // insert adds the ID into the cache if it is currently unavailable.
   233  // Returns true if the ID was added to the cache.
   234  func (c *idCache) insert(id ID) bool {
   235  	if _, ok := c.ids[id]; ok {
   236  		return false
   237  	}
   238  
   239  	if _, exists := c.leased[id]; exists {
   240  		return false
   241  	}
   242  
   243  	c.ids[id] = struct{}{}
   244  	return true
   245  }
   246  
   247  // remove removes the ID from the cache.
   248  // Returns true if the ID was available in the cache.
   249  func (c *idCache) remove(id ID) bool {
   250  	delete(c.leased, id)
   251  
   252  	if _, ok := c.ids[id]; ok {
   253  		delete(c.ids, id)
   254  		return true
   255  	}
   256  
   257  	return false
   258  }