1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-15 22:34:58 +00:00
serenity/Userland/Libraries/LibJS/Heap/HeapBlock.h
Andreas Kling 751ad19c86 LibJS: Don't consider cells in the lazy freelist in conservative scan
Cells after the lazy freelist bump index are guaranteed to not be
valid cell pointers, so ignore them during the conservative scan.
2021-05-17 19:57:40 +02:00

104 lines
2.8 KiB
C++

/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/IntrusiveList.h>
#include <AK/Types.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
namespace JS {
class HeapBlock {
AK_MAKE_NONCOPYABLE(HeapBlock);
AK_MAKE_NONMOVABLE(HeapBlock);
public:
static constexpr size_t block_size = 16 * KiB;
static NonnullOwnPtr<HeapBlock> create_with_cell_size(Heap&, size_t);
void operator delete(void*);
size_t cell_size() const { return m_cell_size; }
size_t cell_count() const { return (block_size - sizeof(HeapBlock)) / m_cell_size; }
bool is_full() const { return !has_lazy_freelist() && !m_freelist; }
ALWAYS_INLINE Cell* allocate()
{
if (m_freelist) {
VERIFY(is_valid_cell_pointer(m_freelist));
return exchange(m_freelist, m_freelist->next);
}
if (has_lazy_freelist())
return cell(m_next_lazy_freelist_index++);
return nullptr;
}
void deallocate(Cell*);
template<typename Callback>
void for_each_cell(Callback callback)
{
auto end = has_lazy_freelist() ? m_next_lazy_freelist_index : cell_count();
for (size_t i = 0; i < end; ++i)
callback(cell(i));
}
Heap& heap() { return m_heap; }
static HeapBlock* from_cell(const Cell* cell)
{
return reinterpret_cast<HeapBlock*>((FlatPtr)cell & ~(block_size - 1));
}
Cell* cell_from_possible_pointer(FlatPtr pointer)
{
if (pointer < reinterpret_cast<FlatPtr>(m_storage))
return nullptr;
size_t cell_index = (pointer - reinterpret_cast<FlatPtr>(m_storage)) / m_cell_size;
auto end = has_lazy_freelist() ? m_next_lazy_freelist_index : cell_count();
if (cell_index >= end)
return nullptr;
return cell(cell_index);
}
bool is_valid_cell_pointer(const Cell* cell)
{
return cell_from_possible_pointer((FlatPtr)cell);
}
IntrusiveListNode<HeapBlock> m_list_node;
private:
HeapBlock(Heap&, size_t cell_size);
bool has_lazy_freelist() const { return m_next_lazy_freelist_index < cell_count(); }
struct FreelistEntry final : public Cell {
FreelistEntry* next { nullptr };
virtual const char* class_name() const override { return "FreelistEntry"; }
};
Cell* cell(size_t index)
{
return reinterpret_cast<Cell*>(&m_storage[index * cell_size()]);
}
FreelistEntry* init_freelist_entry(size_t index)
{
return new (&m_storage[index * cell_size()]) FreelistEntry();
}
Heap& m_heap;
size_t m_cell_size { 0 };
size_t m_next_lazy_freelist_index { 0 };
FreelistEntry* m_freelist { nullptr };
alignas(Cell) u8 m_storage[];
};
}