mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-19 07:33:20 +00:00

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.
166 lines
3.5 KiB
C++
166 lines
3.5 KiB
C++
/*
|
|
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Badge.h>
|
|
#include <AK/RefPtr.h>
|
|
#include <LibGC/Export.h>
|
|
#include <LibGC/Ptr.h>
|
|
|
|
namespace GC {
|
|
|
|
class WeakBlock;
|
|
|
|
class WeakImpl {
|
|
public:
|
|
// NOTE: Null GC::Weaks point at this WeakImpl. This allows Weak to always chase the impl pointer without null-checking it.
|
|
static GC_API WeakImpl the_null_weak_impl;
|
|
|
|
WeakImpl() = default;
|
|
WeakImpl(void* ptr)
|
|
: m_ptr(ptr)
|
|
{
|
|
}
|
|
|
|
void* ptr() const { return m_ptr; }
|
|
void set_ptr(Badge<WeakBlock>, void* ptr) { m_ptr = ptr; }
|
|
|
|
bool operator==(WeakImpl const& other) const { return m_ptr == other.m_ptr; }
|
|
bool operator!=(WeakImpl const& other) const { return m_ptr != other.m_ptr; }
|
|
|
|
void ref() const { ++m_ref_count; }
|
|
void unref() const
|
|
{
|
|
VERIFY(m_ref_count);
|
|
--m_ref_count;
|
|
}
|
|
|
|
size_t ref_count() const { return m_ref_count; }
|
|
|
|
enum class State {
|
|
Allocated,
|
|
Freelist,
|
|
};
|
|
|
|
void set_state(State state) { m_state = state; }
|
|
State state() const { return m_state; }
|
|
|
|
private:
|
|
mutable size_t m_ref_count { 0 };
|
|
State m_state { State::Allocated };
|
|
void* m_ptr { nullptr };
|
|
};
|
|
|
|
template<typename T>
|
|
class Weak {
|
|
public:
|
|
constexpr Weak() = default;
|
|
Weak(nullptr_t) { }
|
|
|
|
Weak(T const* ptr);
|
|
Weak(T const& ptr);
|
|
|
|
template<typename U>
|
|
Weak(Weak<U> const& other)
|
|
requires(IsConvertible<U*, T*>);
|
|
|
|
Weak(Ref<T> const& other);
|
|
|
|
template<typename U>
|
|
Weak(Ref<U> const& other)
|
|
requires(IsConvertible<U*, T*>);
|
|
|
|
template<typename U>
|
|
Weak& operator=(Weak<U> const& other)
|
|
requires(IsConvertible<U*, T*>)
|
|
{
|
|
m_impl = other.impl();
|
|
return *this;
|
|
}
|
|
|
|
Weak& operator=(Ref<T> const& other);
|
|
|
|
template<typename U>
|
|
Weak& operator=(Ref<U> const& other)
|
|
requires(IsConvertible<U*, T*>);
|
|
|
|
Weak& operator=(T const& other);
|
|
|
|
template<typename U>
|
|
Weak& operator=(U const& other)
|
|
requires(IsConvertible<U*, T*>);
|
|
|
|
Weak& operator=(T const* other);
|
|
|
|
template<typename U>
|
|
Weak& operator=(U const* other)
|
|
requires(IsConvertible<U*, T*>);
|
|
|
|
T* operator->() const
|
|
{
|
|
ASSERT(ptr());
|
|
return ptr();
|
|
}
|
|
|
|
[[nodiscard]] T& operator*() const
|
|
{
|
|
ASSERT(ptr());
|
|
return *ptr();
|
|
}
|
|
|
|
Ptr<T> ptr() const { return static_cast<T*>(impl().ptr()); }
|
|
|
|
explicit operator bool() const { return !!ptr(); }
|
|
bool operator!() const { return !ptr(); }
|
|
|
|
operator T*() const { return ptr(); }
|
|
|
|
Ref<T> as_nonnull() const
|
|
{
|
|
ASSERT(ptr());
|
|
return *ptr();
|
|
}
|
|
|
|
WeakImpl& impl() const { return *m_impl; }
|
|
|
|
private:
|
|
NonnullRefPtr<WeakImpl> m_impl { WeakImpl::the_null_weak_impl };
|
|
};
|
|
|
|
template<typename T, typename U>
|
|
inline bool operator==(Weak<T> const& a, Ptr<U> const& b)
|
|
{
|
|
return a.ptr() == b.ptr();
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
inline bool operator==(Weak<T> const& a, Ref<U> const& b)
|
|
{
|
|
return a.ptr() == b.ptr();
|
|
}
|
|
|
|
}
|
|
|
|
namespace AK {
|
|
|
|
template<typename T>
|
|
struct Traits<GC::Weak<T>> : public DefaultTraits<GC::Weak<T>> {
|
|
static unsigned hash(GC::Weak<T> const& value)
|
|
{
|
|
return Traits<T*>::hash(value.ptr());
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Formatter<GC::Weak<T>> : Formatter<T const*> {
|
|
ErrorOr<void> format(FormatBuilder& builder, GC::Weak<T> const& value)
|
|
{
|
|
return Formatter<T const*>::format(builder, value.ptr());
|
|
}
|
|
};
|
|
|
|
}
|