#include "base/allocator/partition_allocator/partition_root.h"
#include <cstdint>
#include "base/allocator/partition_allocator/freeslot_bitmap.h"
#include "base/allocator/partition_allocator/oom.h"
#include "base/allocator/partition_allocator/page_allocator.h"
#include "base/allocator/partition_allocator/partition_address_space.h"
#include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h"
#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h"
#include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
#include "base/allocator/partition_allocator/partition_alloc_check.h"
#include "base/allocator/partition_allocator/partition_alloc_config.h"
#include "base/allocator/partition_allocator/partition_alloc_constants.h"
#include "base/allocator/partition_allocator/partition_bucket.h"
#include "base/allocator/partition_allocator/partition_cookie.h"
#include "base/allocator/partition_allocator/partition_oom.h"
#include "base/allocator/partition_allocator/partition_page.h"
#include "base/allocator/partition_allocator/partition_ref_count.h"
#include "base/allocator/partition_allocator/pkey.h"
#include "base/allocator/partition_allocator/reservation_offset_table.h"
#include "base/allocator/partition_allocator/tagging.h"
#include "build/build_config.h"
uintptr_t g_root_cookie;
#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) && BUILDFLAG(IS_APPLE)
#include "base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.h"
#endif
#if BUILDFLAG(USE_STARSCAN)
#include "base/allocator/partition_allocator/starscan/pcscan.h"
#endif
#if !BUILDFLAG(HAS_64_BIT_POINTERS)
#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
#endif
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "wow64apiset.h"
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include <pthread.h>
#endif
namespace partition_alloc::internal {
#if BUILDFLAG(RECORD_ALLOC_INFO)
AllocInfo g_allocs = {};
void RecordAllocOrFree(uintptr_t addr, size_t size) {
g_allocs.allocs[g_allocs.index.fetch_add(1, std::memory_order_relaxed) %
kAllocInfoSize] = {addr, size};
}
#endif
#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
PtrPosWithinAlloc IsPtrWithinSameAlloc(uintptr_t orig_address,
uintptr_t test_address,
size_t type_size) {
uintptr_t adjusted_address =
orig_address - kPartitionPastAllocationAdjustment;
PA_DCHECK(IsManagedByNormalBucketsOrDirectMap(adjusted_address));
DCheckIfManagedByPartitionAllocBRPPool(adjusted_address);
uintptr_t slot_start = PartitionAllocGetSlotStartInBRPPool(adjusted_address);
adjusted_address = 0;
auto* slot_span = SlotSpanMetadata<ThreadSafe>::FromSlotStart(slot_start);
auto* root = PartitionRoot<ThreadSafe>::FromSlotSpan(slot_span);
PA_DCHECK(root->brp_enabled());
uintptr_t object_addr = root->SlotStartToObjectAddr(slot_start);
uintptr_t object_end = object_addr + slot_span->GetUsableSize(root);
if (test_address < object_addr || object_end < test_address) {
return PtrPosWithinAlloc::kFarOOB;
#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR)
} else if (object_end - type_size < test_address) {
return PtrPosWithinAlloc::kAllocEnd;
#endif
} else {
return PtrPosWithinAlloc::kInBounds;
}
}
#endif
}
namespace partition_alloc {
#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR)
namespace {
internal::Lock g_root_enumerator_lock;
}
template <bool thread_safe>
internal::Lock& PartitionRoot<thread_safe>::GetEnumeratorLock() {
return g_root_enumerator_lock;
}
namespace internal {
class PartitionRootEnumerator {
public:
using EnumerateCallback = void (*)(ThreadSafePartitionRoot* root,
bool in_child);
enum EnumerateOrder {
kNormal,
kReverse,
};
static PartitionRootEnumerator& Instance() {
static PartitionRootEnumerator instance;
return instance;
}
void Enumerate(EnumerateCallback callback,
bool in_child,
EnumerateOrder order) PA_NO_THREAD_SAFETY_ANALYSIS {
if (order == kNormal) {
ThreadSafePartitionRoot* root;
for (root = Head(partition_roots_); root != nullptr;
root = root->next_root) {
callback(root, in_child);
}
} else {
PA_DCHECK(order == kReverse);
ThreadSafePartitionRoot* root;
for (root = Tail(partition_roots_); root != nullptr;
root = root->prev_root) {
callback(root, in_child);
}
}
}
void Register(ThreadSafePartitionRoot* root) {
internal::ScopedGuard guard(ThreadSafePartitionRoot::GetEnumeratorLock());
root->next_root = partition_roots_;
root->prev_root = nullptr;
if (partition_roots_) {
partition_roots_->prev_root = root;
}
partition_roots_ = root;
}
void Unregister(ThreadSafePartitionRoot* root) {
internal::ScopedGuard guard(ThreadSafePartitionRoot::GetEnumeratorLock());
ThreadSafePartitionRoot* prev = root->prev_root;
ThreadSafePartitionRoot* next = root->next_root;
if (prev) {
PA_DCHECK(prev->next_root == root);
prev->next_root = next;
} else {
PA_DCHECK(partition_roots_ == root);
partition_roots_ = next;
}
if (next) {
PA_DCHECK(next->prev_root == root);
next->prev_root = prev;
}
root->next_root = nullptr;
root->prev_root = nullptr;
}
private:
constexpr PartitionRootEnumerator() = default;
ThreadSafePartitionRoot* Head(ThreadSafePartitionRoot* roots) {
return roots;
}
ThreadSafePartitionRoot* Tail(ThreadSafePartitionRoot* roots)
PA_NO_THREAD_SAFETY_ANALYSIS {
if (!roots) {
return nullptr;
}
ThreadSafePartitionRoot* node = roots;
for (; node->next_root != nullptr; node = node->next_root)
;
return node;
}
ThreadSafePartitionRoot* partition_roots_
PA_GUARDED_BY(ThreadSafePartitionRoot::GetEnumeratorLock()) = nullptr;
};
}
#endif
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
namespace {
#if PA_CONFIG(HAS_ATFORK_HANDLER)
void LockRoot(PartitionRoot<internal::ThreadSafe>* root,
bool) PA_NO_THREAD_SAFETY_ANALYSIS {
PA_DCHECK(root);
root->lock_.Acquire();
}
void BeforeForkInParent() PA_NO_THREAD_SAFETY_ANALYSIS {
g_root_enumerator_lock.Acquire();
internal::PartitionRootEnumerator::Instance().Enumerate(
LockRoot, false,
internal::PartitionRootEnumerator::EnumerateOrder::kNormal);
ThreadCacheRegistry::GetLock().Acquire();
}
template <typename T>
void UnlockOrReinit(T& lock, bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS {
if (in_child) {
lock.Reinit();
} else {
lock.Release();
}
}
void UnlockOrReinitRoot(PartitionRoot<internal::ThreadSafe>* root,
bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS {
UnlockOrReinit(root->lock_, in_child);
}
void ReleaseLocks(bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS {
UnlockOrReinit(ThreadCacheRegistry::GetLock(), in_child);
internal::PartitionRootEnumerator::Instance().Enumerate(
UnlockOrReinitRoot, in_child,
internal::PartitionRootEnumerator::EnumerateOrder::kReverse);
UnlockOrReinit(g_root_enumerator_lock, in_child);
}
void AfterForkInParent() {
ReleaseLocks( false);
}
void AfterForkInChild() {
ReleaseLocks( true);
ThreadCacheRegistry::Instance().ForcePurgeAllThreadAfterForkUnsafe();
}
#endif
std::atomic<bool> g_global_init_called;
void PartitionAllocMallocInitOnce() {
bool expected = false;
if (!g_global_init_called.compare_exchange_strong(expected, true)) {
return;
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
int err =
pthread_atfork(BeforeForkInParent, AfterForkInParent, AfterForkInChild);
PA_CHECK(err == 0);
#endif
}
}
#if BUILDFLAG(IS_APPLE)
void PartitionAllocMallocHookOnBeforeForkInParent() {
BeforeForkInParent();
}
void PartitionAllocMallocHookOnAfterForkInParent() {
AfterForkInParent();
}
void PartitionAllocMallocHookOnAfterForkInChild() {
AfterForkInChild();
}
#endif
#endif
namespace internal {
namespace {
constexpr size_t kMaxPurgeableSlotsPerSystemPage = 64;
PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
MinPurgeableSlotSize() {
return SystemPageSize() / kMaxPurgeableSlotsPerSystemPage;
}
}
template <bool thread_safe>
static size_t PartitionPurgeSlotSpan(
internal::SlotSpanMetadata<thread_safe>* slot_span,
bool discard) {
auto* root = PartitionRoot<thread_safe>::FromSlotSpan(slot_span);
const internal::PartitionBucket<thread_safe>* bucket = slot_span->bucket;
size_t slot_size = bucket->slot_size;
if (slot_size < MinPurgeableSlotSize() || !slot_span->num_allocated_slots) {
return 0;
}
size_t bucket_num_slots = bucket->get_slots_per_span();
size_t discardable_bytes = 0;
if (slot_span->CanStoreRawSize()) {
uint32_t utilized_slot_size = static_cast<uint32_t>(
RoundUpToSystemPage(slot_span->GetUtilizedSlotSize()));
discardable_bytes = bucket->slot_size - utilized_slot_size;
if (discardable_bytes && discard) {
uintptr_t slot_span_start =
internal::SlotSpanMetadata<thread_safe>::ToSlotSpanStart(slot_span);
uintptr_t committed_data_end = slot_span_start + utilized_slot_size;
ScopedSyscallTimer timer{root};
DiscardSystemPages(committed_data_end, discardable_bytes);
}
return discardable_bytes;
}
#if defined(PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR)
constexpr size_t kMaxSlotCount =
(PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan) /
MinPurgeableSlotSize();
#elif BUILDFLAG(IS_APPLE) || (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
constexpr size_t kMaxSlotCount =
4 * kMaxPurgeableSlotsPerSystemPage *
internal::kMaxPartitionPagesPerRegularSlotSpan;
PA_CHECK(kMaxSlotCount == (PartitionPageSize() *
internal::kMaxPartitionPagesPerRegularSlotSpan) /
MinPurgeableSlotSize());
#endif
PA_DCHECK(bucket_num_slots <= kMaxSlotCount);
PA_DCHECK(slot_span->num_unprovisioned_slots < bucket_num_slots);
size_t num_slots = bucket_num_slots - slot_span->num_unprovisioned_slots;
char slot_usage[kMaxSlotCount];
#if !BUILDFLAG(IS_WIN)
size_t last_slot = static_cast<size_t>(-1);
#endif
memset(slot_usage, 1, num_slots);
uintptr_t slot_span_start =
SlotSpanMetadata<thread_safe>::ToSlotSpanStart(slot_span);
for (PartitionFreelistEntry* entry = slot_span->get_freelist_head(); entry;
) {
size_t slot_number =
bucket->GetSlotNumber(SlotStartPtr2Addr(entry) - slot_span_start);
PA_DCHECK(slot_number < num_slots);
slot_usage[slot_number] = 0;
#if !BUILDFLAG(IS_WIN)
if (entry->IsEncodedNextPtrZero()) {
last_slot = slot_number;
}
#endif
entry = entry->GetNext(slot_size);
}
size_t truncated_slots = 0;
while (!slot_usage[num_slots - 1]) {
truncated_slots++;
num_slots--;
PA_DCHECK(num_slots);
}
if (truncated_slots) {
size_t unprovisioned_bytes = 0;
uintptr_t begin_addr = slot_span_start + (num_slots * slot_size);
uintptr_t end_addr = begin_addr + (slot_size * truncated_slots);
uintptr_t rounded_up_truncatation_begin_addr =
RoundUpToSystemPage(begin_addr);
while (begin_addr + slot_size <= rounded_up_truncatation_begin_addr) {
begin_addr += slot_size;
PA_DCHECK(truncated_slots);
--truncated_slots;
++num_slots;
}
begin_addr = rounded_up_truncatation_begin_addr;
end_addr = RoundUpToSystemPage(end_addr);
PA_DCHECK(end_addr <= slot_span_start + bucket->get_bytes_per_span());
if (begin_addr < end_addr) {
unprovisioned_bytes = end_addr - begin_addr;
discardable_bytes += unprovisioned_bytes;
}
if (unprovisioned_bytes && discard) {
PA_DCHECK(truncated_slots > 0);
size_t new_unprovisioned_slots =
truncated_slots + slot_span->num_unprovisioned_slots;
PA_DCHECK(new_unprovisioned_slots <= bucket->get_slots_per_span());
slot_span->num_unprovisioned_slots = new_unprovisioned_slots;
internal::PartitionFreelistEntry* head = nullptr;
internal::PartitionFreelistEntry* back = head;
size_t num_new_entries = 0;
for (size_t slot_index = 0; slot_index < num_slots; ++slot_index) {
if (slot_usage[slot_index]) {
continue;
}
auto* entry = PartitionFreelistEntry::EmplaceAndInitNull(
slot_span_start + (slot_size * slot_index));
if (!head) {
head = entry;
back = entry;
} else {
back->SetNext(entry);
back = entry;
}
num_new_entries++;
#if !BUILDFLAG(IS_WIN)
last_slot = slot_index;
#endif
}
slot_span->SetFreelistHead(head);
PA_DCHECK(num_new_entries == num_slots - slot_span->num_allocated_slots);
#if BUILDFLAG(USE_FREESLOT_BITMAP)
FreeSlotBitmapReset(slot_span_start + (slot_size * num_slots), end_addr,
slot_size);
#endif
ScopedSyscallTimer timer{root};
DiscardSystemPages(begin_addr, unprovisioned_bytes);
}
}
if (slot_size < SystemPageSize()) {
return discardable_bytes;
}
for (size_t i = 0; i < num_slots; ++i) {
if (slot_usage[i]) {
continue;
}
uintptr_t begin_addr = slot_span_start + (i * slot_size);
uintptr_t end_addr = begin_addr + slot_size;
bool can_discard_free_list_pointer = false;
#if !BUILDFLAG(IS_WIN)
if (i != last_slot) {
begin_addr += sizeof(internal::PartitionFreelistEntry);
} else {
can_discard_free_list_pointer = true;
}
#else
begin_addr += sizeof(internal::PartitionFreelistEntry);
#endif
uintptr_t rounded_up_begin_addr = RoundUpToSystemPage(begin_addr);
uintptr_t rounded_down_begin_addr = RoundDownToSystemPage(begin_addr);
end_addr = RoundDownToSystemPage(end_addr);
PA_DCHECK(rounded_up_begin_addr <= end_addr);
if (rounded_down_begin_addr < rounded_up_begin_addr && i != 0 &&
!slot_usage[i - 1] && can_discard_free_list_pointer) {
begin_addr = rounded_down_begin_addr;
} else {
begin_addr = rounded_up_begin_addr;
}
if (begin_addr < end_addr) {
size_t partial_slot_bytes = end_addr - begin_addr;
discardable_bytes += partial_slot_bytes;
if (discard) {
ScopedSyscallTimer timer{root};
DiscardSystemPages(begin_addr, partial_slot_bytes);
}
}
}
return discardable_bytes;
}
template <bool thread_safe>
static void PartitionPurgeBucket(
internal::PartitionBucket<thread_safe>* bucket) {
#if defined(OHOS_ENABLE_POINTER_HARDENED)
internal::SlotSpanMetadata<thread_safe>* real_head =
(internal::SlotSpanMetadata<thread_safe>*)EncodeBucket((void*)(bucket->active_slot_spans_head));
if (real_head !=
#else
if (bucket->active_slot_spans_head !=
#endif
internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) {
for (internal::SlotSpanMetadata<thread_safe>* slot_span =
#if defined(OHOS_ENABLE_POINTER_HARDENED)
real_head;
#else
bucket->active_slot_spans_head;
#endif
slot_span; slot_span = slot_span->next_slot_span) {
PA_DCHECK(
slot_span !=
internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span());
PartitionPurgeSlotSpan(slot_span, true);
}
}
}
template <bool thread_safe>
static void PartitionDumpSlotSpanStats(
PartitionBucketMemoryStats* stats_out,
internal::SlotSpanMetadata<thread_safe>* slot_span) {
uint16_t bucket_num_slots = slot_span->bucket->get_slots_per_span();
if (slot_span->is_decommitted()) {
++stats_out->num_decommitted_slot_spans;
return;
}
stats_out->discardable_bytes += PartitionPurgeSlotSpan(slot_span, false);
if (slot_span->CanStoreRawSize()) {
stats_out->active_bytes += static_cast<uint32_t>(slot_span->GetRawSize());
} else {
stats_out->active_bytes +=
(slot_span->num_allocated_slots * stats_out->bucket_slot_size);
}
stats_out->active_count += slot_span->num_allocated_slots;
size_t slot_span_bytes_resident = RoundUpToSystemPage(
(bucket_num_slots - slot_span->num_unprovisioned_slots) *
stats_out->bucket_slot_size);
stats_out->resident_bytes += slot_span_bytes_resident;
if (slot_span->is_empty()) {
stats_out->decommittable_bytes += slot_span_bytes_resident;
++stats_out->num_empty_slot_spans;
} else if (slot_span->is_full()) {
++stats_out->num_full_slot_spans;
} else {
PA_DCHECK(slot_span->is_active());
++stats_out->num_active_slot_spans;
}
}
template <bool thread_safe>
static void PartitionDumpBucketStats(
PartitionBucketMemoryStats* stats_out,
const internal::PartitionBucket<thread_safe>* bucket) {
PA_DCHECK(!bucket->is_direct_mapped());
stats_out->is_valid = false;
#if defined(OHOS_ENABLE_POINTER_HARDENED)
internal::SlotSpanMetadata<thread_safe>* real_head =
(internal::SlotSpanMetadata<thread_safe>*)EncodeBucket((void*)(bucket->active_slot_spans_head));
if (real_head ==
#else
if (bucket->active_slot_spans_head ==
#endif
internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span() &&
!bucket->empty_slot_spans_head && !bucket->decommitted_slot_spans_head &&
!bucket->num_full_slot_spans) {
return;
}
memset(stats_out, '\0', sizeof(*stats_out));
stats_out->is_valid = true;
stats_out->is_direct_map = false;
stats_out->num_full_slot_spans =
static_cast<size_t>(bucket->num_full_slot_spans);
stats_out->bucket_slot_size = bucket->slot_size;
uint16_t bucket_num_slots = bucket->get_slots_per_span();
size_t bucket_useful_storage = stats_out->bucket_slot_size * bucket_num_slots;
stats_out->allocated_slot_span_size = bucket->get_bytes_per_span();
stats_out->active_bytes = bucket->num_full_slot_spans * bucket_useful_storage;
stats_out->active_count = bucket->num_full_slot_spans * bucket_num_slots;
stats_out->resident_bytes =
bucket->num_full_slot_spans * stats_out->allocated_slot_span_size;
for (internal::SlotSpanMetadata<thread_safe>* slot_span =
#if defined(OHOS_ENABLE_POINTER_HARDENED)
(internal::SlotSpanMetadata<thread_safe>*)EncodeBucket((void*)(bucket->empty_slot_spans_head));
#else
bucket->empty_slot_spans_head;
#endif
slot_span; slot_span = slot_span->next_slot_span) {
PA_DCHECK(slot_span->is_empty() || slot_span->is_decommitted());
PartitionDumpSlotSpanStats(stats_out, slot_span);
}
for (internal::SlotSpanMetadata<thread_safe>* slot_span =
#if defined(OHOS_ENABLE_POINTER_HARDENED)
(internal::SlotSpanMetadata<thread_safe>*)EncodeBucket((void*)(bucket->decommitted_slot_spans_head));
#else
bucket->decommitted_slot_spans_head;
#endif
slot_span; slot_span = slot_span->next_slot_span) {
PA_DCHECK(slot_span->is_decommitted());
PartitionDumpSlotSpanStats(stats_out, slot_span);
}
#if defined(OHOS_ENABLE_POINTER_HARDENED)
real_head = (internal::SlotSpanMetadata<thread_safe>*)EncodeBucket((void*)(bucket->active_slot_spans_head));
if (real_head !=
#else
if (bucket->active_slot_spans_head !=
#endif
internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) {
for (internal::SlotSpanMetadata<thread_safe>* slot_span =
#if defined(OHOS_ENABLE_POINTER_HARDENED)
real_head;
#else
bucket->active_slot_spans_head;
#endif
slot_span; slot_span = slot_span->next_slot_span) {
PA_DCHECK(
slot_span !=
internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span());
PartitionDumpSlotSpanStats(stats_out, slot_span);
}
}
}
#if BUILDFLAG(PA_DCHECK_IS_ON)
void DCheckIfManagedByPartitionAllocBRPPool(uintptr_t address) {
PA_DCHECK(IsManagedByPartitionAllocBRPPool(address));
}
#endif
#if BUILDFLAG(ENABLE_PKEYS)
void PartitionAllocPkeyInit(int pkey) {
PkeySettings::settings.enabled = true;
PartitionAddressSpace::InitPkeyPool(pkey);
TagGlobalsWithPkey(pkey);
}
#endif
}
template <bool thread_safe>
[[noreturn]] PA_NOINLINE void PartitionRoot<thread_safe>::OutOfMemory(
size_t size) {
const size_t virtual_address_space_size =
total_size_of_super_pages.load(std::memory_order_relaxed) +
total_size_of_direct_mapped_pages.load(std::memory_order_relaxed);
#if !defined(ARCH_CPU_64_BITS)
const size_t uncommitted_size =
virtual_address_space_size -
total_size_of_committed_pages.load(std::memory_order_relaxed);
if (uncommitted_size > internal::kReasonableSizeOfUnusedPages) {
internal::PartitionOutOfMemoryWithLotsOfUncommitedPages(size);
}
#if BUILDFLAG(IS_WIN)
BOOL is_wow_64 = FALSE;
IsWow64Process(GetCurrentProcess(), &is_wow_64);
const size_t kReasonableVirtualSize = (is_wow_64 ? 2800 : 1024) * 1024 * 1024;
PA_DEBUG_DATA_ON_STACK("is_wow_64", static_cast<size_t>(is_wow_64));
#else
constexpr size_t kReasonableVirtualSize =
(1024 + 512) * 1024 * 1024;
#endif
if (virtual_address_space_size > kReasonableVirtualSize) {
internal::PartitionOutOfMemoryWithLargeVirtualSize(
virtual_address_space_size);
}
#endif
PA_DEBUG_DATA_ON_STACK("va_size", virtual_address_space_size);
PA_DEBUG_DATA_ON_STACK("alloc", get_total_size_of_allocated_bytes());
PA_DEBUG_DATA_ON_STACK("commit", get_total_size_of_committed_pages());
PA_DEBUG_DATA_ON_STACK("size", size);
if (internal::g_oom_handling_function) {
(*internal::g_oom_handling_function)(size);
}
OOM_CRASH(size);
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::DecommitEmptySlotSpans() {
ShrinkEmptySlotSpansRing(0);
PA_DCHECK(empty_slot_spans_dirty_bytes == 0);
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::DestructForTesting() {
PA_CHECK(!flags.with_thread_cache);
auto pool_handle = ChoosePool();
#if BUILDFLAG(ENABLE_PKEYS)
if (pool_handle == internal::kPkeyPoolHandle) {
return;
}
PA_DCHECK(pool_handle < internal::kNumPools);
#else
PA_DCHECK(pool_handle <= internal::kNumPools);
#endif
auto* curr = first_extent;
while (curr != nullptr) {
auto* next = curr->next;
uintptr_t address = SuperPagesBeginFromExtent(curr);
size_t size =
internal::kSuperPageSize * curr->number_of_consecutive_super_pages;
#if !BUILDFLAG(HAS_64_BIT_POINTERS)
internal::AddressPoolManager::GetInstance().MarkUnused(pool_handle, address,
size);
#endif
internal::AddressPoolManager::GetInstance().UnreserveAndDecommit(
pool_handle, address, size);
curr = next;
}
}
#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK)
template <bool thread_safe>
void PartitionRoot<thread_safe>::EnableMac11MallocSizeHackForTesting() {
flags.mac11_malloc_size_hack_enabled_ = true;
}
#endif
#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && !BUILDFLAG(HAS_64_BIT_POINTERS)
namespace {
std::atomic<bool> g_reserve_brp_guard_region_called;
void ReserveBackupRefPtrGuardRegionIfNeeded() {
bool expected = false;
if (!g_reserve_brp_guard_region_called.compare_exchange_strong(expected,
true)) {
return;
}
size_t alignment = internal::PageAllocationGranularity();
uintptr_t requested_address;
memset(&requested_address, internal::kQuarantinedByte,
sizeof(requested_address));
requested_address = RoundDownToPageAllocationGranularity(requested_address);
for (size_t i = 0; i < 4; ++i) {
[[maybe_unused]] uintptr_t allocated_address =
AllocPages(requested_address, alignment, alignment,
PageAccessibilityConfiguration(
PageAccessibilityConfiguration::kInaccessible),
PageTag::kPartitionAlloc);
requested_address += alignment;
}
}
}
#endif
template <bool thread_safe>
void PartitionRoot<thread_safe>::Init(PartitionOptions opts) {
{
#if BUILDFLAG(IS_APPLE)
PA_CHECK((internal::SystemPageSize() == (size_t{1} << 12)) ||
(internal::SystemPageSize() == (size_t{1} << 14)));
#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
PA_CHECK((internal::SystemPageSize() == (size_t{1} << 12)) ||
(internal::SystemPageSize() == (size_t{1} << 14)));
#endif
::partition_alloc::internal::ScopedGuard guard{lock_};
if (initialized) {
return;
}
::partition_alloc::internal::InitializeMTESupportIfNeeded();
#if BUILDFLAG(HAS_64_BIT_POINTERS)
internal::PartitionAddressSpace::Init();
#endif
#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && !BUILDFLAG(HAS_64_BIT_POINTERS)
ReserveBackupRefPtrGuardRegionIfNeeded();
#endif
flags.allow_aligned_alloc =
opts.aligned_alloc == PartitionOptions::AlignedAlloc::kAllowed;
flags.allow_cookie = opts.cookie == PartitionOptions::Cookie::kAllowed;
#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
flags.brp_enabled_ =
opts.backup_ref_ptr == PartitionOptions::BackupRefPtr::kEnabled;
flags.brp_zapping_enabled_ =
opts.backup_ref_ptr_zapping ==
PartitionOptions::BackupRefPtrZapping::kEnabled;
PA_CHECK(!flags.brp_zapping_enabled_ || flags.brp_enabled_);
#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) && BUILDFLAG(IS_APPLE)
flags.mac11_malloc_size_hack_enabled_ =
flags.brp_enabled_ && internal::base::mac::IsOS11();
#endif
#else
PA_CHECK(opts.backup_ref_ptr == PartitionOptions::BackupRefPtr::kDisabled);
#endif
flags.use_configurable_pool =
(opts.use_configurable_pool ==
PartitionOptions::UseConfigurablePool::kIfAvailable) &&
IsConfigurablePoolAvailable();
PA_DCHECK(!flags.use_configurable_pool || IsConfigurablePoolAvailable());
PA_CHECK(!(flags.use_configurable_pool && brp_enabled()));
#if BUILDFLAG(ENABLE_PKEYS)
PA_CHECK(opts.pkey == internal::kDefaultPkey ||
opts.backup_ref_ptr == PartitionOptions::BackupRefPtr::kDisabled);
flags.pkey = opts.pkey;
#endif
#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && \
!BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT)
PA_CHECK(!flags.allow_aligned_alloc || !flags.brp_enabled_);
#endif
#if PA_CONFIG(EXTRAS_REQUIRED)
flags.extras_size = 0;
flags.extras_offset = 0;
if (flags.allow_cookie) {
flags.extras_size += internal::kPartitionCookieSizeAdjustment;
}
if (brp_enabled()) {
flags.extras_size += internal::kPartitionRefCountSizeAdjustment;
flags.extras_offset += internal::kPartitionRefCountOffsetAdjustment;
}
if (opts.add_dummy_ref_count ==
PartitionOptions::AddDummyRefCount::kEnabled) {
PA_CHECK(!brp_enabled());
flags.extras_size += internal::kPartitionRefCountSizeAdjustment;
}
#endif
PA_CHECK(!flags.allow_aligned_alloc || !flags.extras_offset);
flags.quarantine_mode =
#if BUILDFLAG(USE_STARSCAN)
(opts.quarantine == PartitionOptions::Quarantine::kDisallowed
? QuarantineMode::kAlwaysDisabled
: QuarantineMode::kDisabledByDefault);
#else
QuarantineMode::kAlwaysDisabled;
#endif
memset(&sentinel_bucket, 0, sizeof(sentinel_bucket));
sentinel_bucket.active_slot_spans_head =
#if defined(OHOS_ENABLE_POINTER_HARDENED)
(SlotSpan*)EncodeBucket((void*)SlotSpan::get_sentinel_slot_span_non_const());
#else
SlotSpan::get_sentinel_slot_span_non_const();
#endif
inverted_self = ~reinterpret_cast<uintptr_t>(this);
constexpr internal::BucketIndexLookup lookup{};
size_t bucket_index = 0;
while (lookup.bucket_sizes()[bucket_index] !=
internal::kInvalidBucketSize) {
buckets[bucket_index].Init(lookup.bucket_sizes()[bucket_index]);
bucket_index++;
}
PA_DCHECK(bucket_index < internal::kNumBuckets);
for (size_t index = bucket_index; index < internal::kNumBuckets; index++) {
buckets[index].Init(internal::kInvalidBucketSize);
buckets[index].active_slot_spans_head = nullptr;
PA_DCHECK(!buckets[index].is_valid());
}
#if !PA_CONFIG(THREAD_CACHE_SUPPORTED)
flags.with_thread_cache = false;
#else
ThreadCache::EnsureThreadSpecificDataInitialized();
flags.with_thread_cache =
(opts.thread_cache == PartitionOptions::ThreadCache::kEnabled);
if (flags.with_thread_cache) {
ThreadCache::Init(this);
}
#endif
#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR)
internal::PartitionRootEnumerator::Instance().Register(this);
#endif
initialized = true;
}
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
PartitionAllocMallocInitOnce();
#endif
#if BUILDFLAG(ENABLE_PKEYS)
if (flags.pkey != internal::kDefaultPkey) {
internal::PartitionAllocPkeyInit(flags.pkey);
}
#endif
}
template <bool thread_safe>
PartitionRoot<thread_safe>::~PartitionRoot() {
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
PA_CHECK(!flags.with_thread_cache)
<< "Must not destroy a partition with a thread cache";
#endif
#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR)
if (initialized) {
internal::PartitionRootEnumerator::Instance().Unregister(this);
}
#endif
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::EnableThreadCacheIfSupported() {
#if PA_CONFIG(THREAD_CACHE_SUPPORTED)
::partition_alloc::internal::ScopedGuard guard{lock_};
PA_CHECK(!flags.with_thread_cache);
int before =
thread_caches_being_constructed_.fetch_add(1, std::memory_order_acquire);
PA_CHECK(before == 0);
ThreadCache::Init(this);
thread_caches_being_constructed_.fetch_sub(1, std::memory_order_release);
flags.with_thread_cache = true;
#endif
}
template <bool thread_safe>
bool PartitionRoot<thread_safe>::TryReallocInPlaceForDirectMap(
internal::SlotSpanMetadata<thread_safe>* slot_span,
size_t requested_size) {
PA_DCHECK(slot_span->bucket->is_direct_mapped());
PA_DCHECK(
internal::IsManagedByDirectMap(reinterpret_cast<uintptr_t>(slot_span)));
size_t raw_size = AdjustSizeForExtrasAdd(requested_size);
auto* extent = DirectMapExtent::FromSlotSpan(slot_span);
size_t current_reservation_size = extent->reservation_size;
size_t new_reservation_size = GetDirectMapReservationSize(raw_size);
if (new_reservation_size > current_reservation_size) {
return false;
}
if ((new_reservation_size >> internal::SystemPageShift()) * 5 <
(current_reservation_size >> internal::SystemPageShift()) * 4) {
return false;
}
size_t new_slot_size = GetDirectMapSlotSize(raw_size);
if (new_slot_size < internal::kMinDirectMappedDownsize) {
return false;
}
size_t current_slot_size = slot_span->bucket->slot_size;
size_t current_usable_size = slot_span->GetUsableSize(this);
uintptr_t slot_start = SlotSpan::ToSlotSpanStart(slot_span);
size_t available_reservation_size =
current_reservation_size - extent->padding_for_alignment -
PartitionRoot<thread_safe>::GetDirectMapMetadataAndGuardPagesSize();
#if BUILDFLAG(PA_DCHECK_IS_ON)
uintptr_t reservation_start = slot_start & internal::kSuperPageBaseMask;
PA_DCHECK(internal::IsReservationStart(reservation_start));
PA_DCHECK(slot_start + available_reservation_size ==
reservation_start + current_reservation_size -
GetDirectMapMetadataAndGuardPagesSize() +
internal::PartitionPageSize());
#endif
if (new_slot_size == current_slot_size) {
} else if (new_slot_size < current_slot_size) {
size_t decommit_size = current_slot_size - new_slot_size;
DecommitSystemPagesForData(slot_start + new_slot_size, decommit_size,
PageAccessibilityDisposition::kRequireUpdate);
} else if (new_slot_size <= available_reservation_size) {
size_t recommit_slot_size_growth = new_slot_size - current_slot_size;
RecommitSystemPagesForData(slot_start + current_slot_size,
recommit_slot_size_growth,
PageAccessibilityDisposition::kRequireUpdate);
#if BUILDFLAG(PA_DCHECK_IS_ON)
memset(reinterpret_cast<void*>(slot_start + current_slot_size),
internal::kUninitializedByte, recommit_slot_size_growth);
#endif
} else {
return false;
}
DecreaseTotalSizeOfAllocatedBytes(reinterpret_cast<uintptr_t>(slot_span),
slot_span->bucket->slot_size);
slot_span->SetRawSize(raw_size);
slot_span->bucket->slot_size = new_slot_size;
IncreaseTotalSizeOfAllocatedBytes(reinterpret_cast<uintptr_t>(slot_span),
slot_span->bucket->slot_size, raw_size);
auto* thread_cache = GetOrCreateThreadCache();
if (ThreadCache::IsValid(thread_cache)) {
thread_cache->RecordDeallocation(current_usable_size);
thread_cache->RecordAllocation(slot_span->GetUsableSize(this));
}
#if BUILDFLAG(PA_DCHECK_IS_ON)
if (flags.allow_cookie) {
auto* object = static_cast<unsigned char*>(SlotStartToObject(slot_start));
internal::PartitionCookieWriteValue(object +
slot_span->GetUsableSize(this));
}
#endif
return true;
}
template <bool thread_safe>
bool PartitionRoot<thread_safe>::TryReallocInPlaceForNormalBuckets(
void* object,
SlotSpan* slot_span,
size_t new_size) {
uintptr_t slot_start = ObjectToSlotStart(object);
PA_DCHECK(internal::IsManagedByNormalBuckets(slot_start));
if (AllocationCapacityFromRequestedSize(new_size) !=
AllocationCapacityFromSlotStart(slot_start)) {
return false;
}
size_t current_usable_size = slot_span->GetUsableSize(this);
if (slot_span->CanStoreRawSize()) {
#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) && BUILDFLAG(PA_DCHECK_IS_ON)
internal::PartitionRefCount* old_ref_count;
if (brp_enabled()) {
old_ref_count = internal::PartitionRefCountPointer(slot_start);
}
#endif
size_t new_raw_size = AdjustSizeForExtrasAdd(new_size);
slot_span->SetRawSize(new_raw_size);
#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) && BUILDFLAG(PA_DCHECK_IS_ON)
if (brp_enabled()) {
internal::PartitionRefCount* new_ref_count =
internal::PartitionRefCountPointer(slot_start);
PA_DCHECK(new_ref_count == old_ref_count);
}
#endif
#if BUILDFLAG(PA_DCHECK_IS_ON)
if (flags.allow_cookie) {
internal::PartitionCookieWriteValue(static_cast<unsigned char*>(object) +
slot_span->GetUsableSize(this));
}
#endif
}
ThreadCache* thread_cache = GetOrCreateThreadCache();
if (PA_LIKELY(ThreadCache::IsValid(thread_cache))) {
thread_cache->RecordDeallocation(current_usable_size);
thread_cache->RecordAllocation(slot_span->GetUsableSize(this));
}
return object;
}
template <bool thread_safe>
void* PartitionRoot<thread_safe>::ReallocWithFlags(unsigned int flags,
void* ptr,
size_t new_size,
const char* type_name) {
#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
CHECK_MAX_SIZE_OR_RETURN_NULLPTR(new_size, flags);
void* result = realloc(ptr, new_size);
PA_CHECK(result || flags & AllocFlags::kReturnNull);
return result;
#else
bool no_hooks = flags & AllocFlags::kNoHooks;
if (PA_UNLIKELY(!ptr)) {
return no_hooks
? AllocWithFlagsNoHooks(flags, new_size,
internal::PartitionPageSize())
: AllocWithFlagsInternal(
flags, new_size, internal::PartitionPageSize(), type_name);
}
if (PA_UNLIKELY(!new_size)) {
Free(ptr);
return nullptr;
}
if (new_size > internal::MaxDirectMapped()) {
if (flags & AllocFlags::kReturnNull) {
return nullptr;
}
internal::PartitionExcessiveAllocationSize(new_size);
}
const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled();
bool overridden = false;
size_t old_usable_size;
if (PA_UNLIKELY(!no_hooks && hooks_enabled)) {
overridden = PartitionAllocHooks::ReallocOverrideHookIfEnabled(
&old_usable_size, ptr);
}
if (PA_LIKELY(!overridden)) {
SlotSpan* slot_span = SlotSpan::FromObject(ptr);
auto* old_root = PartitionRoot::FromSlotSpan(slot_span);
bool success = false;
bool tried_in_place_for_direct_map = false;
{
::partition_alloc::internal::ScopedGuard guard{old_root->lock_};
PA_DCHECK(IsValidSlotSpan(slot_span));
old_usable_size = slot_span->GetUsableSize(old_root);
if (PA_UNLIKELY(slot_span->bucket->is_direct_mapped())) {
tried_in_place_for_direct_map = true;
success = old_root->TryReallocInPlaceForDirectMap(slot_span, new_size);
}
}
if (success) {
if (PA_UNLIKELY(!no_hooks && hooks_enabled)) {
PartitionAllocHooks::ReallocObserverHookIfEnabled(ptr, ptr, new_size,
type_name);
}
return ptr;
}
if (PA_LIKELY(!tried_in_place_for_direct_map)) {
if (old_root->TryReallocInPlaceForNormalBuckets(ptr, slot_span,
new_size)) {
return ptr;
}
}
}
void* ret =
no_hooks ? AllocWithFlagsNoHooks(flags, new_size,
internal::PartitionPageSize())
: AllocWithFlagsInternal(
flags, new_size, internal::PartitionPageSize(), type_name);
if (!ret) {
if (flags & AllocFlags::kReturnNull) {
return nullptr;
}
internal::PartitionExcessiveAllocationSize(new_size);
}
memcpy(ret, ptr, std::min(old_usable_size, new_size));
Free(ptr);
return ret;
#endif
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::PurgeMemory(int flags) {
{
::partition_alloc::internal::ScopedGuard guard{lock_};
#if BUILDFLAG(USE_STARSCAN)
if (PCScan::IsInProgress()) {
return;
}
#endif
if (flags & PurgeFlags::kDecommitEmptySlotSpans) {
DecommitEmptySlotSpans();
}
if (flags & PurgeFlags::kDiscardUnusedSystemPages) {
for (Bucket& bucket : buckets) {
if (bucket.slot_size == internal::kInvalidBucketSize) {
continue;
}
if (bucket.slot_size >= internal::MinPurgeableSlotSize()) {
internal::PartitionPurgeBucket(&bucket);
} else {
bucket.SortSlotSpanFreelists();
}
bucket.MaintainActiveList();
if (sort_active_slot_spans_) {
bucket.SortActiveSlotSpans();
}
}
}
}
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::ShrinkEmptySlotSpansRing(size_t limit) {
int16_t index = global_empty_slot_span_ring_index;
int16_t starting_index = index;
while (empty_slot_spans_dirty_bytes > limit) {
SlotSpan* slot_span = global_empty_slot_span_ring[index];
if (slot_span) {
slot_span->DecommitIfPossible(this);
global_empty_slot_span_ring[index] = nullptr;
}
index += 1;
if (index == internal::kMaxFreeableSpans) {
index = 0;
}
if (index == starting_index) {
PA_DCHECK(empty_slot_spans_dirty_bytes == 0);
break;
}
}
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::DumpStats(const char* partition_name,
bool is_light_dump,
PartitionStatsDumper* dumper) {
static const size_t kMaxReportableDirectMaps = 4096;
std::unique_ptr<uint32_t[]> direct_map_lengths;
if (!is_light_dump) {
direct_map_lengths =
std::unique_ptr<uint32_t[]>(new uint32_t[kMaxReportableDirectMaps]);
}
PartitionBucketMemoryStats bucket_stats[internal::kNumBuckets];
size_t num_direct_mapped_allocations = 0;
PartitionMemoryStats stats = {0};
stats.syscall_count = syscall_count.load(std::memory_order_relaxed);
stats.syscall_total_time_ns =
syscall_total_time_ns.load(std::memory_order_relaxed);
{
::partition_alloc::internal::ScopedGuard guard{lock_};
PA_DCHECK(total_size_of_allocated_bytes <= max_size_of_allocated_bytes);
stats.total_mmapped_bytes =
total_size_of_super_pages.load(std::memory_order_relaxed) +
total_size_of_direct_mapped_pages.load(std::memory_order_relaxed);
stats.total_committed_bytes =
total_size_of_committed_pages.load(std::memory_order_relaxed);
stats.max_committed_bytes =
max_size_of_committed_pages.load(std::memory_order_relaxed);
stats.total_allocated_bytes = total_size_of_allocated_bytes;
stats.max_allocated_bytes = max_size_of_allocated_bytes;
#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
stats.total_brp_quarantined_bytes =
total_size_of_brp_quarantined_bytes.load(std::memory_order_relaxed);
stats.total_brp_quarantined_count =
total_count_of_brp_quarantined_slots.load(std::memory_order_relaxed);
stats.cumulative_brp_quarantined_bytes =
cumulative_size_of_brp_quarantined_bytes.load(
std::memory_order_relaxed);
stats.cumulative_brp_quarantined_count =
cumulative_count_of_brp_quarantined_slots.load(
std::memory_order_relaxed);
#endif
size_t direct_mapped_allocations_total_size = 0;
for (size_t i = 0; i < internal::kNumBuckets; ++i) {
const Bucket* bucket = &bucket_at(i);
if (!bucket->is_valid()) {
bucket_stats[i].is_valid = false;
} else {
internal::PartitionDumpBucketStats(&bucket_stats[i], bucket);
}
if (bucket_stats[i].is_valid) {
stats.total_resident_bytes += bucket_stats[i].resident_bytes;
stats.total_active_bytes += bucket_stats[i].active_bytes;
stats.total_active_count += bucket_stats[i].active_count;
stats.total_decommittable_bytes += bucket_stats[i].decommittable_bytes;
stats.total_discardable_bytes += bucket_stats[i].discardable_bytes;
}
}
for (DirectMapExtent* extent = direct_map_list;
extent && num_direct_mapped_allocations < kMaxReportableDirectMaps;
extent = extent->next_extent, ++num_direct_mapped_allocations) {
PA_DCHECK(!extent->next_extent ||
extent->next_extent->prev_extent == extent);
size_t slot_size = extent->bucket->slot_size;
direct_mapped_allocations_total_size += slot_size;
if (is_light_dump) {
continue;
}
direct_map_lengths[num_direct_mapped_allocations] = slot_size;
}
stats.total_resident_bytes += direct_mapped_allocations_total_size;
stats.total_active_bytes += direct_mapped_allocations_total_size;
stats.total_active_count += num_direct_mapped_allocations;
stats.has_thread_cache = flags.with_thread_cache;
if (stats.has_thread_cache) {
ThreadCacheRegistry::Instance().DumpStats(
true, &stats.current_thread_cache_stats);
ThreadCacheRegistry::Instance().DumpStats(false,
&stats.all_thread_caches_stats);
}
}
if (!is_light_dump) {
for (auto& stat : bucket_stats) {
if (stat.is_valid) {
dumper->PartitionsDumpBucketStats(partition_name, &stat);
}
}
for (size_t i = 0; i < num_direct_mapped_allocations; ++i) {
uint32_t size = direct_map_lengths[i];
PartitionBucketMemoryStats mapped_stats = {};
mapped_stats.is_valid = true;
mapped_stats.is_direct_map = true;
mapped_stats.num_full_slot_spans = 1;
mapped_stats.allocated_slot_span_size = size;
mapped_stats.bucket_slot_size = size;
mapped_stats.active_bytes = size;
mapped_stats.active_count = 1;
mapped_stats.resident_bytes = size;
dumper->PartitionsDumpBucketStats(partition_name, &mapped_stats);
}
}
dumper->PartitionDumpTotals(partition_name, &stats);
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::DeleteForTesting(
PartitionRoot* partition_root) {
if (partition_root->flags.with_thread_cache) {
ThreadCache::SwapForTesting(nullptr);
partition_root->flags.with_thread_cache = false;
}
partition_root->DestructForTesting();
delete partition_root;
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::ResetForTesting(bool allow_leaks) {
if (flags.with_thread_cache) {
ThreadCache::SwapForTesting(nullptr);
flags.with_thread_cache = false;
}
::partition_alloc::internal::ScopedGuard guard(lock_);
#if BUILDFLAG(PA_DCHECK_IS_ON)
if (!allow_leaks) {
unsigned num_allocated_slots = 0;
for (Bucket& bucket : buckets) {
#if defined(OHOS_ENABLE_POINTER_HARDENED)
internal::SlotSpanMetadata<thread_safe>* real_head =
(internal::SlotSpanMetadata<thread_safe>*)EncodeBucket((void*)(bucket->active_slot_spans_head));
if (real_head !=
#else
if (bucket.active_slot_spans_head !=
#endif
internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) {
for (internal::SlotSpanMetadata<thread_safe>* slot_span =
#if defined(OHOS_ENABLE_POINTER_HARDENED)
real_head;
#else
bucket.active_slot_spans_head;
#endif
slot_span; slot_span = slot_span->next_slot_span) {
num_allocated_slots += slot_span->num_allocated_slots;
}
}
if (bucket.num_full_slot_spans) {
num_allocated_slots +=
bucket.num_full_slot_spans * bucket.get_slots_per_span();
}
}
PA_DCHECK(num_allocated_slots == 0);
PA_DCHECK(!direct_map_list);
}
#endif
DestructForTesting();
#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR)
if (initialized) {
internal::PartitionRootEnumerator::Instance().Unregister(this);
}
#endif
for (Bucket& bucket : buckets) {
bucket.active_slot_spans_head =
#if defined(OHOS_ENABLE_POINTER_HARDENED)
(SlotSpan*)EncodeBucket((void*)SlotSpan::get_sentinel_slot_span_non_const());
#else
SlotSpan::get_sentinel_slot_span_non_const();
#endif
bucket.empty_slot_spans_head = nullptr;
bucket.decommitted_slot_spans_head = nullptr;
bucket.num_full_slot_spans = 0;
}
next_super_page = 0;
next_partition_page = 0;
next_partition_page_end = 0;
current_extent = nullptr;
first_extent = nullptr;
direct_map_list = nullptr;
for (auto& entity : global_empty_slot_span_ring) {
entity = nullptr;
}
global_empty_slot_span_ring_index = 0;
global_empty_slot_span_ring_size = internal::kDefaultEmptySlotSpanRingSize;
initialized = false;
}
template <bool thread_safe>
void PartitionRoot<thread_safe>::ResetBookkeepingForTesting() {
::partition_alloc::internal::ScopedGuard guard{lock_};
max_size_of_allocated_bytes = total_size_of_allocated_bytes;
max_size_of_committed_pages.store(total_size_of_committed_pages);
}
template <>
ThreadCache* PartitionRoot<internal::ThreadSafe>::MaybeInitThreadCache() {
auto* tcache = ThreadCache::Get();
if (ThreadCache::IsTombstone(tcache) ||
thread_caches_being_constructed_.load(std::memory_order_acquire)) {
return nullptr;
}
int before =
thread_caches_being_constructed_.fetch_add(1, std::memory_order_relaxed);
PA_CHECK(before < std::numeric_limits<int>::max());
tcache = ThreadCache::Create(this);
thread_caches_being_constructed_.fetch_sub(1, std::memory_order_relaxed);
return tcache;
}
template <>
void PartitionRoot<internal::ThreadSafe>::EnableSortActiveSlotSpans() {
sort_active_slot_spans_ = true;
}
template struct PA_COMPONENT_EXPORT(PARTITION_ALLOC)
PartitionRoot<internal::ThreadSafe>;
static_assert(offsetof(PartitionRoot<internal::ThreadSafe>, sentinel_bucket) ==
offsetof(PartitionRoot<internal::ThreadSafe>, buckets) +
internal::kNumBuckets *
sizeof(PartitionRoot<internal::ThreadSafe>::Bucket),
"sentinel_bucket must be just after the regular buckets.");
static_assert(
offsetof(PartitionRoot<internal::ThreadSafe>, lock_) >= 64,
"The lock should not be on the same cacheline as the read-mostly flags");
}