github.com/geniusesgroup/libgo@v0.0.0-20220713101832-828057a9d3d4/pool/lifo.go (about) 1 /* For license and copyright information please see LEGAL file in repository */ 2 3 package pool 4 5 import ( 6 "sync" 7 "sync/atomic" 8 "time" 9 10 "../protocol" 11 ) 12 13 // LIFO pool, i.e. the most recently Put() item will return in call Get() 14 // Such a scheme keeps CPU caches hot (in theory). 15 // 16 // Due to LIFO behaviors and MaxItems logic can't implement with sync/atomic like sync.Pool 17 type LIFO struct { 18 MaxItems int64 19 MinItems int64 20 MaxIdles int64 21 IdleTimeout protocol.Duration 22 // New optionally specifies a function to generate 23 // a value when Get would otherwise return nil. 24 // It may not be changed concurrently with calls to Get. 25 New func() interface{} 26 // CloseFunc optionally specifies a function to call when an item want to drop after timeout 27 CloseFunc func(item interface{}) 28 29 itemsCount int64 // idles + actives 30 mutex sync.Mutex // lock below fields until new line 31 hotItem interface{} 32 idleItems []interface{} 33 victimItems []interface{} 34 35 state int32 36 } 37 38 func (p *LIFO) Init() { 39 var preStatus = p.SetState(PoolStatus_Running) 40 if preStatus != PoolStatus_Unset { 41 panic("[pool] LIFO instance can't reuse") 42 } 43 if p.CloseFunc == nil { 44 p.CloseFunc = func(item interface{}) {} 45 } 46 p.idleItems = make([]interface{}, p.MaxItems/4) 47 go p.Clean() 48 } 49 50 func (p *LIFO) State() PoolStatus { return PoolStatus(atomic.LoadInt32(&p.state)) } 51 func (p *LIFO) SetState(state PoolStatus) (pre PoolStatus) { 52 return PoolStatus(atomic.SwapInt32(&p.state, int32(state))) 53 } 54 func (p *LIFO) Len() (ln int) { 55 p.mutex.Lock() 56 ln = len(p.idleItems) + len(p.victimItems) 57 p.mutex.Unlock() 58 return 59 } 60 61 func (p *LIFO) Get() (item interface{}) { 62 if p.isStop() { 63 return 64 } 65 66 item = p.popHead() 67 if item == nil && p.New != nil { 68 item = p.makeNew() 69 } 70 return 71 } 72 73 func (p *LIFO) Put(item interface{}) { 74 if p.isStop() { 75 p.CloseFunc(item) 76 return 77 } 78 79 p.pushHead(item) 80 } 81 82 func (p *LIFO) Close() { 83 var preStatus = p.SetState(PoolStatus_Stopping) 84 if preStatus != PoolStatus_Running { 85 panic("[pool] LIFO instance wasn't started to call Close()") 86 } 87 88 p.mutex.Lock() 89 for i := 0; i < len(p.idleItems); i++ { 90 var item = p.idleItems[i] 91 if item != nil { 92 p.CloseFunc(item) 93 } 94 } 95 p.mutex.Unlock() 96 97 p.SetState(PoolStatus_Stopped) 98 } 99 100 // Clean items but not by exact p.IdleTimeout. To improve performance we choose two window clean up. 101 // Means some idle items can live up to double of p.IdleTimeout 102 func (p *LIFO) Clean() { 103 for { 104 time.Sleep(time.Duration(p.IdleTimeout)) 105 if p.isStop() { 106 break 107 } 108 109 p.mutex.Lock() 110 var vi = p.victimItems 111 p.victimItems = p.idleItems 112 p.idleItems = make([]interface{}, p.MaxItems/4) 113 p.mutex.Unlock() 114 115 atomic.AddInt64(&p.itemsCount, -int64(len(vi))) 116 117 // Close items outside of p.mutex 118 for i := 0; i < len(vi); i++ { 119 p.CloseFunc(vi[i]) 120 } 121 } 122 } 123 124 func (p *LIFO) isStop() bool { 125 var pStatus = p.State() 126 if pStatus == PoolStatus_Stopping || pStatus == PoolStatus_Stopped { 127 return true 128 } 129 return false 130 } 131 132 func (p *LIFO) popHead() (item interface{}) { 133 p.mutex.Lock() 134 if p.hotItem != nil { 135 item = p.hotItem 136 p.hotItem = nil 137 } else { 138 var ln = len(p.idleItems) - 1 139 if ln > -1 { 140 item = p.idleItems[ln] 141 p.idleItems = p.idleItems[:ln] 142 } else { 143 ln = len(p.victimItems) - 1 144 if ln > -1 { 145 item = p.victimItems[ln] 146 p.victimItems = p.victimItems[:ln] 147 } 148 } 149 } 150 p.mutex.Unlock() 151 return 152 } 153 154 func (p *LIFO) pushHead(item interface{}) { 155 p.mutex.Lock() 156 if p.hotItem == nil { 157 p.hotItem = item 158 } else { 159 p.idleItems = append(p.idleItems, item) 160 } 161 p.mutex.Unlock() 162 } 163 164 func (p *LIFO) makeNew() (item interface{}) { 165 var ic = atomic.AddInt64(&p.itemsCount, 1) 166 if ic <= p.MaxItems { 167 item = p.New() 168 } else { 169 atomic.AddInt64(&p.itemsCount, -1) 170 } 171 return 172 }