v8.run/go/exp@v0.0.26-0.20230226010534-afcdbd3f782d/pool2/gopool1/gopool1.go (about) 1 package gopool1 2 3 import ( 4 "errors" 5 "sync" 6 "time" 7 ) 8 9 type timedworker[T any] struct { 10 ch chan T 11 tt int64 12 } 13 14 type GoPool1[T any] struct { 15 maxWorkers int64 16 handler func(T) 17 stop chan struct{} 18 idleTimeout time.Duration 19 gcPeriod time.Duration 20 _ [10]uint64 21 22 mu sync.Mutex 23 workers int64 24 pool sync.Pool 25 dobby []*timedworker[T] 26 } 27 28 func tworker[T any](pool *GoPool1[T], w *timedworker[T]) { 29 for v := range w.ch { 30 pool.handler(v) 31 if pool.free(w) { 32 return 33 } 34 } 35 36 pool.mu.Lock() 37 pool.workers-- 38 pool.mu.Unlock() 39 } 40 41 func (pool *GoPool1[T]) start(preheat int) { 42 pool.pool.New = func() interface{} { 43 return &timedworker[T]{ 44 ch: make(chan T, 1), 45 } 46 } 47 pool.stop = make(chan struct{}) 48 49 if preheat > 0 { 50 pool.workers = int64(preheat) 51 for i := 0; i < preheat; i++ { 52 w := pool.pool.Get().(*timedworker[T]) 53 w.tt = time.Now().UnixNano() 54 pool.dobby = append(pool.dobby, w) 55 go tworker(pool, w) 56 } 57 } 58 59 go func() { 60 ticker := time.NewTicker(pool.gcPeriod) 61 defer ticker.Stop() 62 63 var wbuffer []*timedworker[T] 64 65 for { 66 select { 67 case <-ticker.C: 68 pool.mu.Lock() 69 if pool.maxWorkers == -1 { 70 // Stop All Workers 71 for _, w := range pool.dobby { 72 close(w.ch) 73 } 74 pool.mu.Unlock() 75 return 76 } 77 78 // Stop Idle Workers 79 now := time.Now().Add(-pool.idleTimeout).UnixNano() 80 dc := len(pool.dobby) 81 if dc > 0 { 82 l, r := 0, dc 83 m := (l + r) >> 1 84 for l < r { 85 if pool.dobby[m].tt < now { 86 l = m + 1 87 } else { 88 r = m 89 } 90 m = (l + r) >> 1 91 } 92 93 wbuffer = append(wbuffer, pool.dobby[:l]...) 94 copy(pool.dobby, pool.dobby[l:]) 95 pool.dobby = pool.dobby[:dc-l] 96 } 97 pool.mu.Unlock() 98 99 // Stop Old Workers 100 for i := range wbuffer { 101 close(wbuffer[i].ch) 102 wbuffer[i].tt = 0 103 wbuffer[i].ch = nil 104 pool.pool.Put(wbuffer[i]) 105 wbuffer[i] = nil 106 } 107 wbuffer = wbuffer[:0] 108 109 case <-pool.stop: 110 pool.mu.Lock() 111 pool.maxWorkers = -1 112 for i := range pool.dobby { 113 close(pool.dobby[i].ch) 114 // Drop All Workers 115 // pool.dobby[i].tt = 0 116 // pool.dobby[i].ch = nil 117 // pool.pool.Put(pool.dobby[i]) 118 // pool.dobby[i] = nil 119 } 120 pool.mu.Unlock() 121 return 122 } 123 } 124 }() 125 } 126 127 func (pool *GoPool1[T]) free(w *timedworker[T]) (stop bool) { 128 w.tt = time.Now().UnixNano() 129 pool.mu.Lock() 130 if pool.maxWorkers == -1 { 131 pool.workers-- 132 pool.mu.Unlock() 133 return true 134 } 135 pool.dobby = append(pool.dobby, w) 136 pool.mu.Unlock() 137 return false 138 } 139 140 func (pool *GoPool1[T]) Run(v T) (ok bool) { 141 var w *timedworker[T] 142 pool.mu.Lock() 143 // Check If Pool Is Draining 144 if pool.maxWorkers == -1 { 145 pool.mu.Unlock() 146 return false 147 } 148 149 // Check For Dobby 150 if len(pool.dobby) > 0 { 151 w = pool.dobby[len(pool.dobby)-1] 152 pool.dobby = pool.dobby[:len(pool.dobby)-1] 153 } else { 154 // No Dobby, Create Worker 155 if pool.workers < pool.maxWorkers { 156 pool.workers++ 157 w = pool.pool.Get().(*timedworker[T]) 158 w.ch = make(chan T, 1) 159 go tworker(pool, w) 160 } else { 161 pool.mu.Unlock() 162 return false 163 } 164 } 165 pool.mu.Unlock() 166 167 w.ch <- v 168 return true 169 } 170 171 var ( 172 ErrInvalidMaxWorkers = errors.New("invalid max workers, must be greater than 0") 173 ErrInvalidHandler = errors.New("invalid handler, must not be nil") 174 ErrInvalidIdleTimeout = errors.New("invalid idle timeout, must be greater than 0s") 175 ErrInvalidGCPeriod = errors.New("invalid gc period, must be greater than 0s") 176 ) 177 178 func New[T any](maxWorkers int64, handler func(T), idleTimeout, gcPeriod time.Duration, preheat int) (*GoPool1[T], error) { 179 if maxWorkers <= 0 { 180 return nil, ErrInvalidMaxWorkers 181 } 182 if handler == nil { 183 return nil, ErrInvalidHandler 184 } 185 if idleTimeout <= 0 { 186 return nil, ErrInvalidIdleTimeout 187 } 188 if gcPeriod <= 0 { 189 return nil, ErrInvalidGCPeriod 190 } 191 192 pool := &GoPool1[T]{ 193 maxWorkers: maxWorkers, 194 handler: handler, 195 idleTimeout: idleTimeout, 196 gcPeriod: gcPeriod, 197 } 198 199 pool.start(preheat) 200 return pool, nil 201 } 202 203 func (pool *GoPool1[T]) Stop() { 204 if pool.stop == nil { 205 return 206 } 207 pool.stop <- struct{}{} 208 pool.stop = nil 209 } 210 211 func (pool *GoPool1[T]) Workers() int64 { 212 pool.mu.Lock() 213 w := pool.workers 214 pool.mu.Unlock() 215 return w 216 }