From 7c4d928807a1a46d70b17fbf22c13653f6baa378 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 10 Sep 2025 18:44:37 -0400 Subject: [PATCH] Start on Ref/WeakRef --- src/reference.hpp | 187 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 src/reference.hpp diff --git a/src/reference.hpp b/src/reference.hpp new file mode 100644 index 0000000..c41690e --- /dev/null +++ b/src/reference.hpp @@ -0,0 +1,187 @@ +#pragma once + +#include +#include +#include +#include +#include + +/** + * @brief Thread-safe reference counting abstraction with shared/weak pointer + * semantics + * + * TODO: Implement custom reference counting system with: + * - Thread-safe reference counting using atomic operations + * - Weak reference support to break circular dependencies + * - Move semantics for efficient transfers + * - Custom deleter support + * - Zero-overhead when not using weak references + */ + +namespace detail { +struct ControlBlock { + // Least significant 32 bits are strong reference count + // Most significant 32 bits are weak reference count + std::atomic ref_counts; + + ControlBlock() : ref_counts(1) {} // Start with 1 strong reference + + /** + * @brief Increment strong reference count + * @return Previous ref_counts value (both strong and weak counts) + */ + uint64_t increment_strong() noexcept { + uint64_t old_value; + uint64_t new_value; + do { + old_value = ref_counts.load(std::memory_order_relaxed); + uint32_t strong_count = static_cast(old_value); + uint32_t weak_count = static_cast(old_value >> 32); + new_value = + (static_cast(weak_count) << 32) | (strong_count + 1); + } while (!ref_counts.compare_exchange_weak(old_value, new_value, + std::memory_order_relaxed)); + + return old_value; + } + + /** + * @brief Decrement strong reference count + * @return Previous ref_counts value (both strong and weak counts) + */ + uint64_t decrement_strong() noexcept { + uint64_t old_value; + uint64_t new_value; + do { + old_value = ref_counts.load(std::memory_order_relaxed); + uint32_t strong_count = static_cast(old_value); + uint32_t weak_count = static_cast(old_value >> 32); + new_value = + (static_cast(weak_count) << 32) | (strong_count - 1); + } while (!ref_counts.compare_exchange_weak(old_value, new_value, + std::memory_order_acq_rel)); + + return old_value; + } + + /** + * @brief Increment weak reference count + * @return Previous ref_counts value (both strong and weak counts) + */ + uint64_t increment_weak() noexcept { + uint64_t old_value; + uint64_t new_value; + do { + old_value = ref_counts.load(std::memory_order_relaxed); + uint32_t strong_count = static_cast(old_value); + uint32_t weak_count = static_cast(old_value >> 32); + new_value = (static_cast(weak_count + 1) << 32) | strong_count; + } while (!ref_counts.compare_exchange_weak(old_value, new_value, + std::memory_order_relaxed)); + + return old_value; + } + + /** + * @brief Decrement weak reference count + * @return Previous ref_counts value (both strong and weak counts) + */ + uint64_t decrement_weak() noexcept { + uint64_t old_value; + uint64_t new_value; + do { + old_value = ref_counts.load(std::memory_order_relaxed); + uint32_t strong_count = static_cast(old_value); + uint32_t weak_count = static_cast(old_value >> 32); + new_value = (static_cast(weak_count - 1) << 32) | strong_count; + } while (!ref_counts.compare_exchange_weak(old_value, new_value, + std::memory_order_acq_rel)); + + return old_value; + } +}; +} // namespace detail + +template struct Ref { + T *get() { + if (!control_block) + return nullptr; + constexpr size_t cb_size = sizeof(detail::ControlBlock); + constexpr size_t alignment = alignof(T); + constexpr size_t padded_cb_size = + (cb_size + alignment - 1) & ~(alignment - 1); + return reinterpret_cast(reinterpret_cast(control_block) + + padded_cb_size); + } + +private: + explicit Ref(detail::ControlBlock *cb) : control_block(cb) {} + Ref() : control_block(nullptr) {} + + detail::ControlBlock *control_block; + + template + friend Ref make_ref(Args &&...args); + + template friend struct WeakRef; +}; + +template struct WeakRef { + Ref lock() { + if (!control_block) { + return Ref(); + } + uint64_t old_value; + uint64_t new_value; + do { + // Use acquire ordering to ensure that any subsequent use of the returned + // Ref (like dereferencing the object pointer) cannot be reordered before + // this safety check. This would ideally use memory_order_consume for + // dependency ordering, but the folk wisdom is "don't use that". + old_value = control_block->ref_counts.load(std::memory_order_acquire); + uint32_t strong_count = static_cast(old_value); + + // If strong count is 0, object is being destroyed + if (strong_count == 0) { + return Ref(); + } + + uint32_t weak_count = static_cast(old_value >> 32); + new_value = + (static_cast(weak_count) << 32) | (strong_count + 1); + } while (!control_block->ref_counts.compare_exchange_weak( + old_value, new_value, std::memory_order_relaxed)); + + return Ref(control_block); + } + +private: + explicit WeakRef(detail::ControlBlock *cb) : control_block(cb) {} + WeakRef() : control_block(nullptr) {} + + detail::ControlBlock *control_block; + + template friend struct Ref; +}; + +/** + * @brief Create a new Ref with object constructed in-place after control block + */ +template Ref make_ref(Args &&...args) { + constexpr size_t cb_size = sizeof(detail::ControlBlock); + constexpr size_t alignment = alignof(T); + constexpr size_t padded_cb_size = + (cb_size + alignment - 1) & ~(alignment - 1); + + char *buf = reinterpret_cast( + std::aligned_alloc(std::max(alignof(detail::ControlBlock), alignment), + padded_cb_size + sizeof(T))); + if (!buf) { + std::fprintf(stderr, "Out of memory\n"); + std::abort(); + } + + auto *cb = new (buf) detail::ControlBlock(); + new (buf + padded_cb_size) T{std::forward(args)...}; + return Ref(cb); +}