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