/* * Copyright (c) 2025, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include 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, 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 class Weak { public: constexpr Weak() = default; Weak(nullptr_t) { } Weak(T const* ptr); Weak(T const& ptr); Weak(Weak&& other) : m_impl(exchange(other.m_impl, WeakImpl::the_null_weak_impl)) { } Weak& operator=(Weak&& other) { if (this != &other) { m_impl = exchange(other.m_impl, WeakImpl::the_null_weak_impl); } return *this; } Weak(Weak const&) = default; template Weak(Weak const& other) requires(IsConvertible); Weak(Ref const& other); template Weak(Ref const& other) requires(IsConvertible); Weak& operator=(Weak const& other) = default; template Weak& operator=(Weak const& other) requires(IsConvertible) { m_impl = other.impl(); return *this; } Weak& operator=(Ref const& other); template Weak& operator=(Ref const& other) requires(IsConvertible); Weak& operator=(T const& other); template Weak& operator=(U const& other) requires(IsConvertible); Weak& operator=(T const* other); template Weak& operator=(U const* other) requires(IsConvertible); T* operator->() const { ASSERT(ptr()); return ptr(); } [[nodiscard]] T& operator*() const { ASSERT(ptr()); return *ptr(); } Ptr ptr() const { return static_cast(impl().ptr()); } explicit operator bool() const { return !!ptr(); } bool operator!() const { return !ptr(); } operator T*() const { return ptr(); } Ref as_nonnull() const { ASSERT(ptr()); return *ptr(); } WeakImpl& impl() const { return *m_impl; } private: NonnullRefPtr m_impl { WeakImpl::the_null_weak_impl }; }; // NOTE: Unlike GC::Function, captures in this callback are not visited by the garbage collector. // Do not capture GC-managed objects without ensuring they are kept alive through other means. template auto weak_callback(T& obj, Callback&& callback) { return [weak = Weak { obj }, cb = forward(callback)](auto&&... args) mutable { if (weak) cb(*weak, forward(args)...); }; } template inline bool operator==(Weak const& a, Ptr const& b) { return a.ptr() == b.ptr(); } template inline bool operator==(Weak const& a, Ref const& b) { return a.ptr() == b.ptr(); } } namespace AK { template struct Formatter> : Formatter { ErrorOr format(FormatBuilder& builder, GC::Weak const& value) { return Formatter::format(builder, value.ptr()); } }; }