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