ladybird/Libraries/LibGC/WeakBlock.cpp
Andreas Kling 25a5ed94d6 LibGC: Add GC::Weak<T> as an alternative to AK::WeakPtr<T>
This is a weak pointer that integrates with the garbage collector.
It has a number of differences compared to AK::WeakPtr, including:

- The "control block" is allocated from a well-packed WeakBlock owned by
  the GC heap, not just a generic malloc allocation.

- Pointers to dead cells are nulled out by the garbage collector
  immediately before running destructors.

- It works on any GC::Cell derived type, meaning you don't have to
  inherit from AK::Weakable for the ability to be weakly referenced.

- The Weak always points to a control block, even when "null" (it then
  points to a null WeakImpl), which means one less null check when
  chasing pointers.
2025-10-17 17:22:16 +02:00

77 lines
1.9 KiB
C++

/*
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGC/Cell.h>
#include <LibGC/WeakBlock.h>
#include <sys/mman.h>
#if defined(AK_OS_WINDOWS)
# include <AK/Windows.h>
# include <memoryapi.h>
#endif
namespace GC {
WeakImpl WeakImpl::the_null_weak_impl;
WeakBlock* WeakBlock::create()
{
#if !defined(AK_OS_WINDOWS)
auto* block = (HeapBlock*)mmap(nullptr, WeakBlock::BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
VERIFY(block != MAP_FAILED);
#else
auto* block = (HeapBlock*)VirtualAlloc(NULL, WeakBlock::BLOCK_SIZE, MEM_COMMIT, PAGE_READWRITE);
VERIFY(block);
#endif
return new (block) WeakBlock;
}
WeakBlock::WeakBlock()
{
for (size_t i = 0; i < IMPL_COUNT; ++i) {
m_impls[i].set_ptr({}, i + 1 < IMPL_COUNT ? &m_impls[i + 1] : nullptr);
m_impls[i].set_state(WeakImpl::State::Freelist);
}
m_freelist = &m_impls[0];
}
WeakBlock::~WeakBlock() = default;
WeakImpl* WeakBlock::allocate(Cell* cell)
{
auto* impl = m_freelist;
if (!impl)
return nullptr;
VERIFY(impl->ref_count() == 0);
m_freelist = impl->ptr() ? static_cast<WeakImpl*>(impl->ptr()) : nullptr;
impl->set_ptr({}, cell);
impl->set_state(WeakImpl::State::Allocated);
return impl;
}
void WeakBlock::deallocate(WeakImpl* impl)
{
VERIFY(impl->ref_count() == 0);
impl->set_ptr({}, m_freelist);
impl->set_state(WeakImpl::State::Freelist);
m_freelist = impl;
}
void WeakBlock::sweep()
{
for (size_t i = 0; i < IMPL_COUNT; ++i) {
auto& impl = m_impls[i];
if (impl.state() == WeakImpl::State::Freelist)
continue;
auto* cell = static_cast<Cell*>(impl.ptr());
if (!cell || !cell->is_marked())
impl.set_ptr({}, nullptr);
if (impl.ref_count() == 0)
deallocate(&impl);
}
}
}