diff options
author | Lei Zhang <thestig@chromium.org> | 2018-10-16 18:40:15 +0000 |
---|---|---|
committer | Chromium commit bot <commit-bot@chromium.org> | 2018-10-16 18:40:15 +0000 |
commit | f1fae3cd1b9492cf9411dffd3c486b9672d8cc76 (patch) | |
tree | bee5d8b2528e714149d399163519f0fa35ec532b /third_party/base/allocator/partition_allocator/partition_page.cc | |
parent | 1b6fcaad0722fb586620efc974726dee3fb0f80f (diff) | |
download | pdfium-f1fae3cd1b9492cf9411dffd3c486b9672d8cc76.tar.xz |
Update PartitionAlloc from Chromium at r599712.
BUG=pdfium:1170
Change-Id: I0f8dfb3d517beaa682a9ca7ad4831c5a7a10dc3b
Reviewed-on: https://pdfium-review.googlesource.com/c/44073
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Diffstat (limited to 'third_party/base/allocator/partition_allocator/partition_page.cc')
-rw-r--r-- | third_party/base/allocator/partition_allocator/partition_page.cc | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/third_party/base/allocator/partition_allocator/partition_page.cc b/third_party/base/allocator/partition_allocator/partition_page.cc new file mode 100644 index 0000000000..3f70048269 --- /dev/null +++ b/third_party/base/allocator/partition_allocator/partition_page.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/base/allocator/partition_allocator/partition_page.h" + +#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h" +#include "third_party/base/allocator/partition_allocator/partition_root_base.h" + +namespace pdfium { +namespace base { +namespace internal { + +namespace { + +ALWAYS_INLINE void PartitionDirectUnmap(PartitionPage* page) { + PartitionRootBase* root = PartitionRootBase::FromPage(page); + const PartitionDirectMapExtent* extent = + PartitionDirectMapExtent::FromPage(page); + size_t unmap_size = extent->map_size; + + // Maintain the doubly-linked list of all direct mappings. + if (extent->prev_extent) { + DCHECK(extent->prev_extent->next_extent == extent); + extent->prev_extent->next_extent = extent->next_extent; + } else { + root->direct_map_list = extent->next_extent; + } + if (extent->next_extent) { + DCHECK(extent->next_extent->prev_extent == extent); + extent->next_extent->prev_extent = extent->prev_extent; + } + + // Add on the size of the trailing guard page and preceeding partition + // page. + unmap_size += kPartitionPageSize + kSystemPageSize; + + size_t uncommitted_page_size = page->bucket->slot_size + kSystemPageSize; + root->DecreaseCommittedPages(uncommitted_page_size); + DCHECK(root->total_size_of_direct_mapped_pages >= uncommitted_page_size); + root->total_size_of_direct_mapped_pages -= uncommitted_page_size; + + DCHECK(!(unmap_size & kPageAllocationGranularityOffsetMask)); + + char* ptr = reinterpret_cast<char*>(PartitionPage::ToPointer(page)); + // Account for the mapping starting a partition page before the actual + // allocation address. + ptr -= kPartitionPageSize; + + FreePages(ptr, unmap_size); +} + +ALWAYS_INLINE void PartitionRegisterEmptyPage(PartitionPage* page) { + DCHECK(page->is_empty()); + PartitionRootBase* root = PartitionRootBase::FromPage(page); + + // If the page is already registered as empty, give it another life. + if (page->empty_cache_index != -1) { + DCHECK(page->empty_cache_index >= 0); + DCHECK(static_cast<unsigned>(page->empty_cache_index) < kMaxFreeableSpans); + DCHECK(root->global_empty_page_ring[page->empty_cache_index] == page); + root->global_empty_page_ring[page->empty_cache_index] = nullptr; + } + + int16_t current_index = root->global_empty_page_ring_index; + PartitionPage* page_to_decommit = root->global_empty_page_ring[current_index]; + // The page might well have been re-activated, filled up, etc. before we get + // around to looking at it here. + if (page_to_decommit) + page_to_decommit->DecommitIfPossible(root); + + // We put the empty slot span on our global list of "pages that were once + // empty". thus providing it a bit of breathing room to get re-used before + // we really free it. This improves performance, particularly on Mac OS X + // which has subpar memory management performance. + root->global_empty_page_ring[current_index] = page; + page->empty_cache_index = current_index; + ++current_index; + if (current_index == kMaxFreeableSpans) + current_index = 0; + root->global_empty_page_ring_index = current_index; +} + +} // namespace + +// static +PartitionPage PartitionPage::sentinel_page_; + +PartitionPage* PartitionPage::get_sentinel_page() { + return &sentinel_page_; +} + +void PartitionPage::FreeSlowPath() { + DCHECK(this != get_sentinel_page()); + if (LIKELY(this->num_allocated_slots == 0)) { + // Page became fully unused. + if (UNLIKELY(bucket->is_direct_mapped())) { + PartitionDirectUnmap(this); + return; + } + // If it's the current active page, change it. We bounce the page to + // the empty list as a force towards defragmentation. + if (LIKELY(this == bucket->active_pages_head)) + bucket->SetNewActivePage(); + DCHECK(bucket->active_pages_head != this); + + set_raw_size(0); + DCHECK(!get_raw_size()); + + PartitionRegisterEmptyPage(this); + } else { + DCHECK(!bucket->is_direct_mapped()); + // Ensure that the page is full. That's the only valid case if we + // arrive here. + DCHECK(this->num_allocated_slots < 0); + // A transition of num_allocated_slots from 0 to -1 is not legal, and + // likely indicates a double-free. + CHECK(this->num_allocated_slots != -1); + this->num_allocated_slots = -this->num_allocated_slots - 2; + DCHECK(this->num_allocated_slots == bucket->get_slots_per_span() - 1); + // Fully used page became partially used. It must be put back on the + // non-full page list. Also make it the current page to increase the + // chances of it being filled up again. The old current page will be + // the next page. + DCHECK(!this->next_page); + if (LIKELY(bucket->active_pages_head != get_sentinel_page())) + this->next_page = bucket->active_pages_head; + bucket->active_pages_head = this; + --bucket->num_full_pages; + // Special case: for a partition page with just a single slot, it may + // now be empty and we want to run it through the empty logic. + if (UNLIKELY(this->num_allocated_slots == 0)) + FreeSlowPath(); + } +} + +void PartitionPage::Decommit(PartitionRootBase* root) { + DCHECK(is_empty()); + DCHECK(!bucket->is_direct_mapped()); + void* addr = PartitionPage::ToPointer(this); + root->DecommitSystemPages(addr, bucket->get_bytes_per_span()); + + // We actually leave the decommitted page in the active list. We'll sweep + // it on to the decommitted page list when we next walk the active page + // list. + // Pulling this trick enables us to use a singly-linked page list for all + // cases, which is critical in keeping the page metadata structure down to + // 32 bytes in size. + freelist_head = nullptr; + num_unprovisioned_slots = 0; + DCHECK(is_decommitted()); +} + +void PartitionPage::DecommitIfPossible(PartitionRootBase* root) { + DCHECK(empty_cache_index >= 0); + DCHECK(static_cast<unsigned>(empty_cache_index) < kMaxFreeableSpans); + DCHECK(this == root->global_empty_page_ring[empty_cache_index]); + empty_cache_index = -1; + if (is_empty()) + Decommit(root); +} + +} // namespace internal +} // namespace base +} // namespace pdfium |