github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/lib/pool/pool.go (about) 1 // Package pool implements a memory pool similar in concept to 2 // sync.Pool but with more determinism. 3 package pool 4 5 import ( 6 "fmt" 7 "log" 8 "sync" 9 "time" 10 11 "github.com/rclone/rclone/lib/mmap" 12 ) 13 14 // Pool of internal buffers 15 // 16 // We hold buffers in cache. Every time we Get or Put we update 17 // minFill which is the minimum len(cache) seen. 18 // 19 // Every flushTime we remove minFill buffers from the cache as they 20 // were not used in the previous flushTime interval. 21 type Pool struct { 22 mu sync.Mutex 23 cache [][]byte 24 minFill int // the minimum fill of the cache 25 bufferSize int 26 poolSize int 27 timer *time.Timer 28 inUse int 29 alloced int 30 flushTime time.Duration 31 flushPending bool 32 alloc func(int) ([]byte, error) 33 free func([]byte) error 34 } 35 36 // New makes a buffer pool 37 // 38 // flushTime is the interval the buffer pools is flushed 39 // bufferSize is the size of the allocations 40 // poolSize is the maximum number of free buffers in the pool 41 // useMmap should be set to use mmap allocations 42 func New(flushTime time.Duration, bufferSize, poolSize int, useMmap bool) *Pool { 43 bp := &Pool{ 44 cache: make([][]byte, 0, poolSize), 45 poolSize: poolSize, 46 flushTime: flushTime, 47 bufferSize: bufferSize, 48 } 49 if useMmap { 50 bp.alloc = mmap.Alloc 51 bp.free = mmap.Free 52 } else { 53 bp.alloc = func(size int) ([]byte, error) { 54 return make([]byte, size), nil 55 } 56 bp.free = func([]byte) error { 57 return nil 58 } 59 } 60 bp.timer = time.AfterFunc(flushTime, bp.flushAged) 61 return bp 62 } 63 64 // get gets the last buffer in bp.cache 65 // 66 // Call with mu held 67 func (bp *Pool) get() []byte { 68 n := len(bp.cache) - 1 69 buf := bp.cache[n] 70 bp.cache[n] = nil // clear buffer pointer from bp.cache 71 bp.cache = bp.cache[:n] 72 return buf 73 } 74 75 // put puts the buffer on the end of bp.cache 76 // 77 // Call with mu held 78 func (bp *Pool) put(buf []byte) { 79 bp.cache = append(bp.cache, buf) 80 } 81 82 // flush n entries from the entire buffer pool 83 // Call with mu held 84 func (bp *Pool) flush(n int) { 85 for i := 0; i < n; i++ { 86 bp.freeBuffer(bp.get()) 87 } 88 bp.minFill = len(bp.cache) 89 } 90 91 // Flush the entire buffer pool 92 func (bp *Pool) Flush() { 93 bp.mu.Lock() 94 bp.flush(len(bp.cache)) 95 bp.mu.Unlock() 96 } 97 98 // Remove bp.minFill buffers 99 func (bp *Pool) flushAged() { 100 bp.mu.Lock() 101 bp.flushPending = false 102 bp.flush(bp.minFill) 103 // If there are still items in the cache, schedule another flush 104 if len(bp.cache) != 0 { 105 bp.kickFlusher() 106 } 107 bp.mu.Unlock() 108 } 109 110 // InUse returns the number of buffers in use which haven't been 111 // returned to the pool 112 func (bp *Pool) InUse() int { 113 bp.mu.Lock() 114 defer bp.mu.Unlock() 115 return bp.inUse 116 } 117 118 // InPool returns the number of buffers in the pool 119 func (bp *Pool) InPool() int { 120 bp.mu.Lock() 121 defer bp.mu.Unlock() 122 return len(bp.cache) 123 } 124 125 // Alloced returns the number of buffers allocated and not yet freed 126 func (bp *Pool) Alloced() int { 127 bp.mu.Lock() 128 defer bp.mu.Unlock() 129 return bp.alloced 130 } 131 132 // starts or resets the buffer flusher timer - call with mu held 133 func (bp *Pool) kickFlusher() { 134 if bp.flushPending { 135 return 136 } 137 bp.flushPending = true 138 bp.timer.Reset(bp.flushTime) 139 } 140 141 // Make sure minFill is correct - call with mu held 142 func (bp *Pool) updateMinFill() { 143 if len(bp.cache) < bp.minFill { 144 bp.minFill = len(bp.cache) 145 } 146 } 147 148 // Get a buffer from the pool or allocate one 149 func (bp *Pool) Get() []byte { 150 bp.mu.Lock() 151 var buf []byte 152 waitTime := time.Millisecond 153 for { 154 if len(bp.cache) > 0 { 155 buf = bp.get() 156 break 157 } else { 158 var err error 159 buf, err = bp.alloc(bp.bufferSize) 160 if err == nil { 161 bp.alloced++ 162 break 163 } 164 log.Printf("Failed to get memory for buffer, waiting for %v: %v", waitTime, err) 165 bp.mu.Unlock() 166 time.Sleep(waitTime) 167 bp.mu.Lock() 168 waitTime *= 2 169 } 170 } 171 bp.inUse++ 172 bp.updateMinFill() 173 bp.mu.Unlock() 174 return buf 175 } 176 177 // freeBuffer returns mem to the os if required - call with lock held 178 func (bp *Pool) freeBuffer(mem []byte) { 179 err := bp.free(mem) 180 if err != nil { 181 log.Printf("Failed to free memory: %v", err) 182 } 183 bp.alloced-- 184 } 185 186 // Put returns the buffer to the buffer cache or frees it 187 // 188 // Note that if you try to return a buffer of the wrong size to Put it 189 // will panic. 190 func (bp *Pool) Put(buf []byte) { 191 bp.mu.Lock() 192 defer bp.mu.Unlock() 193 buf = buf[0:cap(buf)] 194 if len(buf) != bp.bufferSize { 195 panic(fmt.Sprintf("Returning buffer sized %d but expecting %d", len(buf), bp.bufferSize)) 196 } 197 if len(bp.cache) < bp.poolSize { 198 bp.put(buf) 199 } else { 200 bp.freeBuffer(buf) 201 } 202 bp.inUse-- 203 bp.updateMinFill() 204 bp.kickFlusher() 205 }