ladybird/Libraries/LibGC/BlockAllocator.cpp
Andreas Kling 716e5f72f2 LibGC: Always use 16 KiB as HeapBlock size
Before this change, we'd use the system page size as the HeapBlock
size. This caused it to vary on different platforms, going as low
as 4 KiB on most Linux systems.

To make this work, we now use posix_memalign() to ensure we get
size-aligned allocations on every platform.

Also nice: HeapBlock::BLOCK_SIZE is now a constant.
2025-12-19 20:21:07 -06:00

94 lines
2.5 KiB
C++

/*
* Copyright (c) 2021-2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Assertions.h>
#include <AK/Format.h>
#include <AK/Platform.h>
#include <AK/Random.h>
#include <AK/Vector.h>
#include <LibGC/BlockAllocator.h>
#include <LibGC/HeapBlock.h>
#include <sys/mman.h>
#ifdef HAS_ADDRESS_SANITIZER
# include <sanitizer/asan_interface.h>
# include <sanitizer/lsan_interface.h>
#endif
#if defined(AK_OS_WINDOWS)
# include <AK/Windows.h>
# include <memoryapi.h>
#endif
namespace GC {
BlockAllocator::~BlockAllocator()
{
for (auto* block : m_blocks) {
ASAN_UNPOISON_MEMORY_REGION(block, HeapBlock::BLOCK_SIZE);
#if !defined(AK_OS_WINDOWS)
free(block);
#else
if (!VirtualFree(block, 0, MEM_RELEASE)) {
warnln("{}", Error::from_windows_error());
VERIFY_NOT_REACHED();
}
#endif
}
}
void* BlockAllocator::allocate_block([[maybe_unused]] char const* name)
{
if (!m_blocks.is_empty()) {
// To reduce predictability, take a random block from the cache.
size_t random_index = get_random_uniform(m_blocks.size());
auto* block = m_blocks.unstable_take(random_index);
ASAN_UNPOISON_MEMORY_REGION(block, HeapBlock::BLOCK_SIZE);
LSAN_REGISTER_ROOT_REGION(block, HeapBlock::BLOCK_SIZE);
return block;
}
#if !defined(AK_OS_WINDOWS)
void* block = nullptr;
auto rc = posix_memalign(&block, HeapBlock::BLOCK_SIZE, HeapBlock::BLOCK_SIZE);
VERIFY(rc == 0);
#else
auto* block = VirtualAlloc(NULL, HeapBlock::BLOCK_SIZE, MEM_COMMIT, PAGE_READWRITE);
VERIFY(block);
#endif
LSAN_REGISTER_ROOT_REGION(block, HeapBlock::BLOCK_SIZE);
return block;
}
void BlockAllocator::deallocate_block(void* block)
{
VERIFY(block);
#if defined(AK_OS_WINDOWS)
DWORD ret = DiscardVirtualMemory(block, HeapBlock::BLOCK_SIZE);
if (ret != ERROR_SUCCESS) {
warnln("{}", Error::from_windows_error(ret));
VERIFY_NOT_REACHED();
}
#elif defined(MADV_FREE)
if (madvise(block, HeapBlock::BLOCK_SIZE, MADV_FREE) < 0) {
perror("madvise(MADV_FREE)");
VERIFY_NOT_REACHED();
}
#elif defined(MADV_DONTNEED)
if (madvise(block, HeapBlock::BLOCK_SIZE, MADV_DONTNEED) < 0) {
perror("madvise(MADV_DONTNEED)");
VERIFY_NOT_REACHED();
}
#endif
ASAN_POISON_MEMORY_REGION(block, HeapBlock::BLOCK_SIZE);
LSAN_UNREGISTER_ROOT_REGION(block, HeapBlock::BLOCK_SIZE);
m_blocks.append(block);
}
}