github.com/searKing/golang/go@v1.2.74/exp/sync/fixedpool.go (about) 1 // Copyright 2022 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sync 6 7 import ( 8 "context" 9 "runtime" 10 "sync" 11 "sync/atomic" 12 "time" 13 14 "github.com/searKing/golang/go/exp/container/queue" 15 math_ "github.com/searKing/golang/go/exp/math" 16 "github.com/searKing/golang/go/pragma" 17 ) 18 19 const ( 20 starvationThresholdNs = 1e6 21 localCMaxSize = 16 22 23 UnlimitedResident = -1 24 UnlimitedCapacity = 0 25 ) 26 27 // FixedPool is a set of resident and temporary items that may be individually saved and 28 // retrieved. 29 // 30 // Any item stored in the Pool may be removed automatically at any time without 31 // notification. If the Pool holds the only reference when this happens, the 32 // item might be deallocated. 33 // 34 // A Pool is safe for use by multiple goroutines simultaneously. 35 type FixedPool[E any] struct { 36 noCopy pragma.DoNotCopy 37 38 length atomic.Int64 // items available 39 capacity atomic.Int64 // items allocated 40 41 // fixed-size pool for keep-alive 42 // localC + localQ 43 localC chan *FixedPoolElement[E] // fixed-size pool for keep-alive 44 mu sync.Mutex 45 localQ queue.Queue[*FixedPoolElement[E]] // temporary pool for allocated, <keep-alive> excluded 46 47 factory sync.Pool // specifies a function to generate a value 48 factoryVictim sync.Pool // A second GC should drop the victim cache, try put into local first. 49 50 pinChan chan struct{} // bell for Put or New 51 52 // New optionally specifies a function to generate 53 // a value when Get would otherwise return nil. 54 // It may not be changed concurrently with calls to Get. 55 New func() E 56 57 // MinResidentSize controls the minimum number of keep-alive items. items will be prealloced. 58 MinResidentSize int 59 // MaxResidentSize controls the maximum number of keep-alive items. Negative means no limit. 60 MaxResidentSize int 61 // MaxCapacity controls the maximum number of allocated items. Zero means no limit. 62 MaxCapacity int 63 } 64 65 // NewFixedPool returns an initialized fixed pool. 66 // resident controls the maximum number of keep-alive items. Negative means no limit. 67 // cap controls the maximum number of allocated items. Zero means no limit. 68 func NewFixedPool[E any](f func() E, size int) *FixedPool[E] { 69 p := &FixedPool[E]{ 70 New: f, 71 MinResidentSize: size, 72 MaxResidentSize: size, 73 MaxCapacity: size, 74 } 75 return p.Init() 76 } 77 78 // NewCachedPool Creates a pool that creates new items as needed, but 79 // will reuse previously constructed items when they are available. 80 // the pool will reuse previously constructed items and items will never be dropped. 81 func NewCachedPool[E any](f func() E) *FixedPool[E] { 82 p := &FixedPool[E]{ 83 New: f, 84 MinResidentSize: 0, 85 MaxResidentSize: UnlimitedResident, 86 MaxCapacity: UnlimitedCapacity, 87 } 88 return p.Init() 89 } 90 91 // NewTempPool Creates a pool that creates new items as needed, but 92 // will be dropped at second GC if only referenced by the pool self. 93 // the pool will reuse previously constructed items when they are available and not dropped. 94 func NewTempPool[E any](f func() E) *FixedPool[E] { 95 p := &FixedPool[E]{ 96 New: f, 97 MinResidentSize: 0, 98 MaxResidentSize: 0, 99 MaxCapacity: UnlimitedCapacity, 100 } 101 return p.Init() 102 } 103 104 // Init initializes fixed pool l. 105 func (p *FixedPool[E]) Init() *FixedPool[E] { 106 if p.MaxResidentSize < 0 { 107 p.MaxCapacity = 0 108 } else { 109 p.MaxCapacity = math_.Max(p.MaxCapacity, p.MaxResidentSize, 0) 110 } 111 p.pinChan = make(chan struct{}) 112 113 p.localC = make(chan *FixedPoolElement[E], math_.Min(localCMaxSize, math_.Max(p.MaxResidentSize, 0))) 114 if p.New != nil && p.factory.New == nil { 115 p.factory.New = func() any { 116 return newFixedPoolElement(p.New(), p) 117 } 118 } 119 120 p.preallocAllResident() 121 return p 122 } 123 124 func (p *FixedPool[E]) Finalize() { 125 // no need for a finalizer anymore 126 runtime.SetFinalizer(p, nil) 127 } 128 129 func (p *FixedPool[E]) preallocAllResident() { 130 if p.New != nil { 131 var xs []*FixedPoolElement[E] 132 for i := 0; i < p.MinResidentSize; i++ { 133 x := p.TryGet() 134 // short circuit 135 if x == nil { 136 break 137 } 138 xs = append(xs, x) 139 } 140 for i := 0; i < len(xs); i++ { 141 p.Put(xs[i]) 142 } 143 } 144 } 145 146 func (p *FixedPool[E]) signal() { 147 select { 148 case p.pinChan <- struct{}{}: 149 break 150 default: 151 break 152 } 153 } 154 155 // Len returns the len of pool, that is object len idle, allocated and still in cache 156 // The complexity is O(1). 157 func (p *FixedPool[E]) Len() int { 158 return int(p.length.Load()) 159 } 160 161 // Cap returns the capacity of pool, that is object len allocated 162 // The complexity is O(1). 163 func (p *FixedPool[E]) Cap() int { 164 return int(p.capacity.Load()) 165 } 166 167 // Emplace adds x to the pool. 168 // NOTE: Emplace may break through the len and cap boundaries, as x be allocated already. 169 func (p *FixedPool[E]) Emplace(x E) { 170 p.Put(newFixedPoolElement(x, p).markAvailable(false)) 171 } 172 173 // Put adds x to the pool. 174 func (p *FixedPool[E]) Put(x *FixedPoolElement[E]) (stored bool) { 175 return p.put(x, true) 176 } 177 178 func (p *FixedPool[E]) TryPut(x *FixedPoolElement[E]) (stored bool) { 179 return p.put(x, false) 180 } 181 182 func (p *FixedPool[E]) put(x *FixedPoolElement[E], victim bool) (stored bool) { 183 if x == nil { 184 return 185 } 186 x.markAvailable(true) 187 defer p.signal() 188 select { 189 case p.localC <- x: 190 return true 191 default: 192 break 193 } 194 return p.putSlow(x, victim) 195 } 196 197 // Get selects an arbitrary item from the Pool, removes it from the 198 // Pool, and returns it to the caller. 199 // Get may choose to ignore the pool and treat it as empty. 200 // Callers should not assume any relation between values passed to Put and 201 // the values returned by Get. 202 // 203 // If Get would otherwise return nil and p.New is non-nil, Get returns 204 // the result of calling p.New. 205 // 206 // Get uses context.Background internally; to specify the context, use 207 // GetContext. 208 func (p *FixedPool[E]) Get() *FixedPoolElement[E] { 209 e, _ := p.GetContext(context.Background()) 210 return e 211 } 212 213 // GetContext selects an arbitrary item from the Pool, removes it from the 214 // Pool, and returns it to the caller. 215 // Get may choose to ignore the pool and treat it as empty. 216 // Callers should not assume any relation between values passed to Put and 217 // the values returned by Get. 218 // 219 // If GetContext would otherwise return nil and p.New is non-nil, Get returns 220 // the result of calling p.New. 221 func (p *FixedPool[E]) GetContext(ctx context.Context) (*FixedPoolElement[E], error) { 222 return p.get(ctx, -1) 223 } 224 225 func (p *FixedPool[E]) TryGet() *FixedPoolElement[E] { 226 e, _ := p.get(context.Background(), 1) 227 return e 228 } 229 230 func (p *FixedPool[E]) get(ctx context.Context, maxIter int) (*FixedPoolElement[E], error) { 231 select { 232 case e := <-p.localC: 233 return e.markAvailable(false), nil 234 case <-ctx.Done(): 235 return nil, ctx.Err() 236 default: 237 if p.localQ.Next() { 238 p.mu.Lock() 239 if p.localQ.Next() { 240 e := p.localQ.PopFront() 241 p.mu.Unlock() 242 return e.markAvailable(false), nil 243 } 244 p.mu.Unlock() 245 } 246 break 247 } 248 return p.getSlow(ctx, maxIter) 249 } 250 251 func (p *FixedPool[E]) getSlow(ctx context.Context, maxIter int) (*FixedPoolElement[E], error) { 252 if ctx == nil { 253 panic("sync.FixedPool: nil Context") 254 } 255 timer := time.NewTimer(starvationThresholdNs) 256 defer timer.Stop() 257 iter := 0 258 for { 259 x, allocated := p.tryAllocate() 260 iter++ 261 if allocated { 262 return x.markAvailable(false), nil 263 } 264 265 select { 266 case e := <-p.localC: 267 return e.markAvailable(false), nil 268 case <-ctx.Done(): 269 return nil, ctx.Err() 270 default: 271 if p.localQ.Next() { 272 p.mu.Lock() 273 if p.localQ.Next() { 274 e := p.localQ.PopFront() 275 p.mu.Unlock() 276 return e.markAvailable(false), nil 277 } 278 p.mu.Unlock() 279 } 280 break 281 } 282 if maxIter > 0 && iter >= maxIter { 283 return nil, nil 284 } 285 if !timer.Stop() { 286 <-timer.C 287 } 288 timer.Reset(starvationThresholdNs) 289 select { 290 case e := <-p.localC: 291 return e.markAvailable(false), nil 292 case <-p.pinChan: 293 break 294 case <-ctx.Done(): 295 return nil, ctx.Err() 296 case <-timer.C: 297 break 298 } 299 } 300 } 301 302 func (p *FixedPool[E]) putSlow(x *FixedPoolElement[E], victim bool) (stored bool) { 303 if x == nil { 304 return true 305 } 306 307 p.mu.Lock() 308 defer p.mu.Unlock() 309 x.markAvailable(true) 310 select { 311 case p.localC <- x: 312 return true 313 default: 314 break 315 } 316 317 move := p.moveToVictimLocked() 318 if move { 319 if victim { 320 // After one GC, the victim cache should keep them alive. 321 // A second GC should drop the victim cache. 322 p.factoryVictim.Put(x) 323 return true 324 } 325 return false 326 } 327 select { 328 case p.localC <- x: 329 return true 330 default: 331 p.localQ.PushBack(x) 332 return true 333 } 334 } 335 336 func (p *FixedPool[E]) isCapacityUnLimited() bool { 337 return p.MaxCapacity <= UnlimitedCapacity 338 } 339 func (p *FixedPool[E]) isResidentUnLimited() bool { 340 return p.MaxResidentSize <= UnlimitedResident 341 } 342 func (p *FixedPool[E]) moveToVictimLocked() bool { 343 // resident no limit 344 if p.isResidentUnLimited() { 345 return false 346 } 347 cap := p.Cap() 348 // cap and resident both has limit 349 if cap > p.MaxResidentSize { 350 return true 351 } 352 return false 353 } 354 355 func (p *FixedPool[E]) tryAllocate() (x *FixedPoolElement[E], allocated bool) { 356 // Try to pop the head of the victim for temporal locality of 357 // reuse. 358 { 359 x := p.factoryVictim.Get() 360 if x != nil { 361 return x.(*FixedPoolElement[E]), true 362 } 363 } 364 365 if p.MaxCapacity <= 0 || p.Cap() < p.MaxCapacity { 366 if p.Cap() < p.MaxCapacity { 367 x := p.factory.Get() 368 if x != nil { 369 return x.(*FixedPoolElement[E]), true 370 } 371 } 372 return nil, true 373 } 374 return nil, false 375 } 376 377 type FixedPoolElement[E any] struct { 378 // The value stored with this element. 379 Value E 380 381 available bool 382 pool *FixedPool[E] 383 } 384 385 func newFixedPoolElement[E any](Value E, pool *FixedPool[E]) *FixedPoolElement[E] { 386 e := &FixedPoolElement[E]{ 387 Value: Value, 388 pool: pool, 389 } 390 e.pool.capacity.Add(1) 391 runtime.SetFinalizer(e, (*FixedPoolElement[E]).Finalize) 392 return e.markAvailable(true) 393 } 394 395 func (e *FixedPoolElement[E]) Finalize() { 396 stored := e.pool.putSlow(e, false) 397 if stored { 398 runtime.SetFinalizer(e, (*FixedPoolElement[E]).Finalize) 399 return 400 } 401 e.markAvailable(false).pool.capacity.Add(-1) 402 // no need for a finalizer anymore 403 runtime.SetFinalizer(e, nil) 404 } 405 406 func (e *FixedPoolElement[E]) Get() E { 407 if e == nil { 408 var zeroE E 409 return zeroE 410 } 411 return e.Value 412 } 413 414 func (e *FixedPoolElement[E]) markAvailable(available bool) *FixedPoolElement[E] { 415 if e != nil { 416 if available == e.available { 417 return e 418 } 419 e.available = available 420 if e.available { 421 e.pool.length.Add(1) 422 } else { 423 e.pool.length.Add(-1) 424 } 425 } 426 return e 427 }