| 370 | } |
| 371 | |
| 372 | bool BufferManager::reserve(uint64_t sizeToReserve) { |
| 373 | // Reserve the memory for the page. |
| 374 | usedMemory += sizeToReserve; |
| 375 | uint64_t totalClaimedMemory = 0; |
| 376 | uint64_t nonEvictableClaimedMemory = 0; |
| 377 | const auto needMoreMemory = [&]() { |
| 378 | // The only time we should exceed the buffer pool size should be when threads are currently |
| 379 | // attempting to reserve space and have pre-allocated space. So if we've claimed enough |
| 380 | // space for what we're trying to reserve, then we can continue even if the current total is |
| 381 | // higher than the buffer pool size as we should never actually exceed the buffer pool size. |
| 382 | return sizeToReserve > totalClaimedMemory && |
| 383 | // usedMemory - totalClaimedMemory could underflow |
| 384 | usedMemory > bufferPoolSize.load() - totalClaimedMemory; |
| 385 | }; |
| 386 | uint8_t failedCount = 0; |
| 387 | // Evict pages if necessary until we have enough memory. |
| 388 | while (needMoreMemory()) { |
| 389 | uint64_t memoryClaimed = 0; |
| 390 | // Avoid reducing the evictable memory below 1/2 at first to reduce thrashing if most of the |
| 391 | // memory is non-evictable |
| 392 | if (!spiller || usedMemory - nonEvictableMemory > bufferPoolSize / 2) { |
| 393 | memoryClaimed = evictPages(); |
| 394 | } else { |
| 395 | auto [_memoryClaimed, nowEvictableMemory] = spiller->claimNextGroup(); |
| 396 | memoryClaimed = _memoryClaimed; |
| 397 | nonEvictableClaimedMemory += _memoryClaimed; |
| 398 | nonEvictableMemory -= nowEvictableMemory; |
| 399 | // If we're unable to claim anything from the spiller, fall back to evicting pages |
| 400 | // We may also need to evict pages if the spiller just unpins BM pages |
| 401 | if (memoryClaimed == 0 || nowEvictableMemory > 0) { |
| 402 | memoryClaimed = evictPages(); |
| 403 | } |
| 404 | } |
| 405 | if (memoryClaimed == 0 && needMoreMemory()) { |
| 406 | if (failedCount++ < 2) { |
| 407 | // If we failed to find any memory to free, try waiting briefly for other threads to |
| 408 | // stop using memory |
| 409 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); |
| 410 | } else { |
| 411 | // Cannot find more pages to be evicted. Free the memory we reserved and return |
| 412 | // false. |
| 413 | freeUsedMemory(sizeToReserve + totalClaimedMemory); |
| 414 | nonEvictableMemory -= nonEvictableClaimedMemory; |
| 415 | return false; |
| 416 | } |
| 417 | } |
| 418 | totalClaimedMemory += memoryClaimed; |
| 419 | } |
| 420 | // Have enough memory available now |
| 421 | if (totalClaimedMemory > 0) { |
| 422 | freeUsedMemory(totalClaimedMemory); |
| 423 | nonEvictableMemory -= nonEvictableClaimedMemory; |
| 424 | } |
| 425 | return true; |
| 426 | } |
| 427 | |
| 428 | uint64_t BufferManager::tryEvictPage(std::atomic<EvictionCandidate>& _candidate) { |
| 429 | auto candidate = _candidate.load(); |
no test coverage detected