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 }