| 628 | } |
| 629 | |
| 630 | pair<int64_t, int64_t> BufferPool::FreeBufferArena::FreeSystemMemory( |
| 631 | int64_t target_bytes_to_free, int64_t target_bytes_to_claim, |
| 632 | std::unique_lock<SpinLock>* arena_lock) { |
| 633 | DCHECK_GT(target_bytes_to_free, 0); |
| 634 | DCHECK_GE(target_bytes_to_free, target_bytes_to_claim); |
| 635 | int64_t bytes_freed = 0; |
| 636 | // If the caller is acquiring the lock, just lock for the whole method. |
| 637 | // Otherwise lazily acquire the lock the first time we find some memory |
| 638 | // to free. |
| 639 | std::unique_lock<SpinLock> al(lock_, std::defer_lock_t()); |
| 640 | if (arena_lock != nullptr) al.lock(); |
| 641 | |
| 642 | vector<BufferHandle> buffers; |
| 643 | // Search from largest to smallest to avoid freeing many small buffers unless |
| 644 | // necessary. |
| 645 | for (int i = NumBufferSizes() - 1; i >= 0; --i) { |
| 646 | PerSizeLists* lists = &buffer_sizes_[i]; |
| 647 | // Check before acquiring lock to avoid expensive lock acquisition and make scanning |
| 648 | // empty lists much cheaper. |
| 649 | if (lists->num_free_buffers.Load() == 0 && lists->num_clean_pages.Load() == 0) { |
| 650 | continue; |
| 651 | } |
| 652 | if (!al.owns_lock()) al.lock(); |
| 653 | FreeList* free_buffers = &lists->free_buffers; |
| 654 | InternalList<Page>* clean_pages = &lists->clean_pages; |
| 655 | DCHECK_EQ(lists->num_free_buffers.Load(), free_buffers->Size()); |
| 656 | DCHECK_EQ(lists->num_clean_pages.Load(), clean_pages->size()); |
| 657 | |
| 658 | // Figure out how many of the buffers in the free list we should free. |
| 659 | DCHECK_GT(target_bytes_to_free, bytes_freed); |
| 660 | const int64_t buffer_len = 1L << (i + parent_->log_min_buffer_len_); |
| 661 | int64_t buffers_to_free = min(free_buffers->Size(), |
| 662 | BitUtil::Ceil(target_bytes_to_free - bytes_freed, buffer_len)); |
| 663 | int64_t buffer_bytes_to_free = buffers_to_free * buffer_len; |
| 664 | |
| 665 | // Evict clean pages by moving their buffers to the free page list before freeing |
| 666 | // them. This ensures that they are freed based on memory address in the expected |
| 667 | // order. |
| 668 | int num_pages_evicted = 0; |
| 669 | int64_t page_bytes_evicted = 0; |
| 670 | while (bytes_freed + buffer_bytes_to_free < target_bytes_to_free) { |
| 671 | Page* page = clean_pages->Dequeue(); |
| 672 | if (page == nullptr) break; |
| 673 | BufferHandle page_buffer; |
| 674 | { |
| 675 | lock_guard<SpinLock> pl(page->buffer_lock); |
| 676 | page_buffer = move(page->buffer); |
| 677 | } |
| 678 | ++buffers_to_free; |
| 679 | buffer_bytes_to_free += page_buffer.len(); |
| 680 | ++num_pages_evicted; |
| 681 | page_bytes_evicted += page_buffer.len(); |
| 682 | free_buffers->AddFreeBuffer(move(page_buffer)); |
| 683 | } |
| 684 | lists->num_free_buffers.Add(num_pages_evicted); |
| 685 | lists->num_clean_pages.Add(-num_pages_evicted); |
| 686 | parent_->clean_page_bytes_remaining_.Add(page_bytes_evicted); |
| 687 |
no test coverage detected