github.com/t2y/goofys@v0.19.1-0.20190123053037-27053313e616/internal/buffer_pool.go (about) 1 // Copyright 2015 - 2017 Ka-Hing Cheung 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package internal 16 17 import ( 18 "io" 19 "runtime" 20 "runtime/debug" 21 "sync" 22 23 "github.com/jacobsa/fuse" 24 "github.com/shirou/gopsutil/mem" 25 ) 26 27 type BufferPool struct { 28 mu sync.Mutex 29 cond *sync.Cond 30 31 numBuffers uint64 32 maxBuffers uint64 33 34 totalBuffers uint64 35 computedMaxbuffers uint64 36 37 pool *sync.Pool 38 } 39 40 const BUF_SIZE = 5 * 1024 * 1024 41 42 func maxMemToUse(buffersNow uint64) uint64 { 43 m, err := mem.VirtualMemory() 44 if err != nil { 45 panic(err) 46 } 47 48 log.Debugf("amount of available memory: %v", m.Available/1024/1024) 49 50 var ms runtime.MemStats 51 runtime.ReadMemStats(&ms) 52 53 log.Debugf("amount of allocated memory: %v %v", ms.Sys/1024/1024, ms.Alloc/1024/1024) 54 55 max := uint64(m.Available+ms.Sys) / 2 56 maxbuffers := MaxUInt64(max/BUF_SIZE, 1) 57 log.Debugf("using up to %v %vMB buffers, now is %v", maxbuffers, BUF_SIZE/1024/1024, buffersNow) 58 return maxbuffers 59 } 60 61 func rounduUp(size uint64, pageSize int) int { 62 return pages(size, pageSize) * pageSize 63 } 64 65 func pages(size uint64, pageSize int) int { 66 return int((size + uint64(pageSize) - 1) / uint64(pageSize)) 67 } 68 69 func (pool BufferPool) Init() *BufferPool { 70 pool.cond = sync.NewCond(&pool.mu) 71 72 pool.computedMaxbuffers = pool.maxBuffers 73 pool.pool = &sync.Pool{New: func() interface{} { 74 return make([]byte, 0, BUF_SIZE) 75 }} 76 77 return &pool 78 } 79 80 // for testing 81 func NewBufferPool(maxSizeGlobal uint64) *BufferPool { 82 pool := BufferPool{maxBuffers: maxSizeGlobal / BUF_SIZE}.Init() 83 return pool 84 } 85 86 func (pool *BufferPool) RequestBuffer() (buf []byte) { 87 return pool.RequestMultiple(BUF_SIZE, true)[0] 88 } 89 90 func (pool *BufferPool) recomputeBufferLimit() { 91 if pool.maxBuffers == 0 { 92 pool.computedMaxbuffers = maxMemToUse(pool.numBuffers) 93 if pool.computedMaxbuffers == 0 { 94 panic("OOM") 95 } 96 } 97 } 98 99 func (pool *BufferPool) RequestMultiple(size uint64, block bool) (buffers [][]byte) { 100 nPages := pages(size, BUF_SIZE) 101 102 pool.mu.Lock() 103 defer pool.mu.Unlock() 104 105 if pool.totalBuffers%10 == 0 { 106 pool.recomputeBufferLimit() 107 } 108 109 for pool.numBuffers+uint64(nPages) > pool.computedMaxbuffers { 110 if block { 111 if pool.numBuffers == 0 { 112 pool.MaybeGC() 113 pool.recomputeBufferLimit() 114 if pool.numBuffers+uint64(nPages) > pool.computedMaxbuffers { 115 // we don't have any in use buffers, and we've made attempts to 116 // free memory AND correct our limits, yet we still can't allocate. 117 // it's likely that we are simply asking for too much 118 log.Errorf("Unable to allocate %d bytes, limit is %d bytes", 119 nPages*BUF_SIZE, pool.computedMaxbuffers*BUF_SIZE) 120 panic("OOM") 121 } 122 } 123 pool.cond.Wait() 124 } else { 125 return 126 } 127 } 128 129 for i := 0; i < nPages; i++ { 130 pool.numBuffers++ 131 pool.totalBuffers++ 132 buf := pool.pool.Get() 133 buffers = append(buffers, buf.([]byte)) 134 } 135 return 136 } 137 138 func (pool *BufferPool) MaybeGC() { 139 if pool.numBuffers == 0 { 140 debug.FreeOSMemory() 141 } 142 } 143 144 func (pool *BufferPool) Free(buf []byte) { 145 pool.mu.Lock() 146 defer pool.mu.Unlock() 147 148 buf = buf[:0] 149 pool.pool.Put(buf) 150 pool.numBuffers-- 151 pool.cond.Signal() 152 } 153 154 var mbufLog = GetLogger("mbuf") 155 156 type MBuf struct { 157 pool *BufferPool 158 buffers [][]byte 159 rbuf int 160 wbuf int 161 rp int 162 wp int 163 } 164 165 func (mb MBuf) Init(h *BufferPool, size uint64, block bool) *MBuf { 166 mb.pool = h 167 168 if size != 0 { 169 mb.buffers = h.RequestMultiple(size, block) 170 if mb.buffers == nil { 171 return nil 172 } 173 } 174 175 return &mb 176 } 177 178 func (mb *MBuf) Len() (length int) { 179 for i := mb.rbuf; i < int(len(mb.buffers)); i++ { 180 var bufSize int 181 var start int 182 if i == mb.wbuf { 183 bufSize = mb.wp 184 } else { 185 bufSize = int(len(mb.buffers[i])) 186 } 187 188 if i == mb.rbuf { 189 start = mb.rp 190 } else { 191 start = 0 192 } 193 194 length += bufSize - start 195 } 196 197 return 198 } 199 200 // seek only seeks the reader 201 func (mb *MBuf) Seek(offset int64, whence int) (int64, error) { 202 switch whence { 203 case 0: // relative to beginning 204 if offset == 0 { 205 mb.rbuf = 0 206 mb.rp = 0 207 return 0, nil 208 } 209 case 1: // relative to current position 210 if offset == 0 { 211 for i := 0; i < mb.rbuf; i++ { 212 offset += int64(len(mb.buffers[i])) 213 } 214 offset += int64(mb.rp) 215 return offset, nil 216 } 217 218 case 2: // relative to the end 219 if offset == 0 { 220 for i := 0; i < len(mb.buffers); i++ { 221 offset += int64(len(mb.buffers[i])) 222 } 223 mb.rbuf = len(mb.buffers) 224 mb.rp = 0 225 return offset, nil 226 } 227 } 228 229 log.Errorf("Seek %d %d", offset, whence) 230 panic(fuse.EINVAL) 231 232 return 0, fuse.EINVAL 233 } 234 235 func (mb *MBuf) Read(p []byte) (n int, err error) { 236 if mb.rbuf == mb.wbuf && mb.rp == mb.wp { 237 err = io.EOF 238 return 239 } 240 241 if mb.rp == cap(mb.buffers[mb.rbuf]) { 242 mb.rbuf++ 243 mb.rp = 0 244 } 245 246 if mb.rbuf == len(mb.buffers) { 247 err = io.EOF 248 return 249 } else if mb.rbuf > len(mb.buffers) { 250 panic("mb.cur > len(mb.buffers)") 251 } 252 253 n = copy(p, mb.buffers[mb.rbuf][mb.rp:]) 254 mb.rp += n 255 256 return 257 } 258 259 func (mb *MBuf) Full() bool { 260 return mb.buffers == nil || (mb.wp == cap(mb.buffers[mb.wbuf]) && mb.wbuf+1 == len(mb.buffers)) 261 } 262 263 func (mb *MBuf) Write(p []byte) (n int, err error) { 264 b := mb.buffers[mb.wbuf] 265 266 if mb.wp == cap(b) { 267 if mb.wbuf+1 == len(mb.buffers) { 268 return 269 } 270 mb.wbuf++ 271 b = mb.buffers[mb.wbuf] 272 mb.wp = 0 273 } else if mb.wp > cap(b) { 274 panic("mb.wp > cap(b)") 275 } 276 277 n = copy(b[mb.wp:cap(b)], p) 278 mb.wp += n 279 // resize the buffer to account for what we just read 280 mb.buffers[mb.wbuf] = mb.buffers[mb.wbuf][:mb.wp] 281 282 return 283 } 284 285 func (mb *MBuf) WriteFrom(r io.Reader) (n int, err error) { 286 b := mb.buffers[mb.wbuf] 287 288 if mb.wp == cap(b) { 289 if mb.wbuf+1 == len(mb.buffers) { 290 return 291 } 292 mb.wbuf++ 293 b = mb.buffers[mb.wbuf] 294 mb.wp = 0 295 } else if mb.wp > cap(b) { 296 panic("mb.wp > cap(b)") 297 } 298 299 n, err = r.Read(b[mb.wp:cap(b)]) 300 mb.wp += n 301 // resize the buffer to account for what we just read 302 mb.buffers[mb.wbuf] = mb.buffers[mb.wbuf][:mb.wp] 303 304 return 305 } 306 307 func (mb *MBuf) Free() { 308 for _, b := range mb.buffers { 309 mb.pool.Free(b) 310 } 311 312 mb.buffers = nil 313 } 314 315 var bufferLog = GetLogger("buffer") 316 317 type Buffer struct { 318 mu sync.Mutex 319 cond *sync.Cond 320 321 buf *MBuf 322 reader io.ReadCloser 323 err error 324 } 325 326 type ReaderProvider func() (io.ReadCloser, error) 327 328 func (b Buffer) Init(buf *MBuf, r ReaderProvider) *Buffer { 329 b.buf = buf 330 b.cond = sync.NewCond(&b.mu) 331 332 go func() { 333 b.readLoop(r) 334 }() 335 336 return &b 337 } 338 339 func (b *Buffer) readLoop(r ReaderProvider) { 340 for { 341 b.mu.Lock() 342 if b.reader == nil { 343 b.reader, b.err = r() 344 b.cond.Broadcast() 345 if b.err != nil { 346 b.mu.Unlock() 347 break 348 } 349 } 350 351 if b.buf == nil { 352 // buffer was drained 353 b.mu.Unlock() 354 break 355 } 356 357 nread, err := b.buf.WriteFrom(b.reader) 358 if err != nil { 359 b.err = err 360 b.mu.Unlock() 361 break 362 } 363 bufferLog.Debugf("wrote %v into buffer", nread) 364 365 if nread == 0 { 366 b.reader.Close() 367 b.mu.Unlock() 368 break 369 } 370 371 b.mu.Unlock() 372 // if we get here we've read _something_, bounce this goroutine 373 // to allow another one to read 374 runtime.Gosched() 375 } 376 bufferLog.Debugf("<-- readLoop()") 377 } 378 379 func (b *Buffer) readFromStream(p []byte) (n int, err error) { 380 bufferLog.Debugf("reading %v from stream", len(p)) 381 382 n, err = b.reader.Read(p) 383 if n != 0 && err == io.ErrUnexpectedEOF { 384 err = nil 385 } else { 386 bufferLog.Debugf("read %v from stream", n) 387 } 388 return 389 } 390 391 func (b *Buffer) Read(p []byte) (n int, err error) { 392 bufferLog.Debugf("Buffer.Read(%v)", len(p)) 393 394 b.mu.Lock() 395 defer b.mu.Unlock() 396 397 for b.reader == nil && b.err == nil { 398 bufferLog.Debugf("waiting for stream") 399 b.cond.Wait() 400 if b.err != nil { 401 err = b.err 402 return 403 } 404 } 405 406 // we could have received the err before Read was called 407 if b.reader == nil { 408 if b.err == nil { 409 panic("reader and err are both nil") 410 } 411 err = b.err 412 return 413 } 414 415 if b.buf != nil { 416 bufferLog.Debugf("reading %v from buffer", len(p)) 417 418 n, err = b.buf.Read(p) 419 if n == 0 { 420 b.buf.Free() 421 b.buf = nil 422 bufferLog.Debugf("drained buffer") 423 n, err = b.readFromStream(p) 424 } else { 425 bufferLog.Debugf("read %v from buffer", n) 426 } 427 } else if b.err != nil { 428 err = b.err 429 } else { 430 n, err = b.readFromStream(p) 431 } 432 433 return 434 } 435 436 func (b *Buffer) Close() (err error) { 437 b.mu.Lock() 438 defer b.mu.Unlock() 439 440 if b.reader != nil { 441 err = b.reader.Close() 442 } 443 444 if b.buf != nil { 445 b.buf.Free() 446 b.buf = nil 447 } 448 449 return 450 }