#pragma once #include #include #include #include #include /** * @brief High-performance thread-safe reference counting system * * This library provides custom smart pointers with shared/weak semantics, * designed for better performance than std::shared_ptr/weak_ptr. * * Key features: * - Thread-safe reference counting using atomic operations * - Weak references to break circular dependencies * - Single allocation for better cache locality * - Optimized for copy/move operations * - Compatible with std::shared_ptr semantics * * Basic usage: * @code * auto obj = make_ref(args...); // Create managed object * auto copy = obj; // Copy (thread-safe) * WeakRef weak = obj; // Create weak reference * auto locked = weak.lock(); // Try to promote to strong * @endcode * * Thread safety: All operations are thread-safe. Multiple threads can * safely copy, move, and destroy references to the same object. */ namespace detail { struct ControlBlock { std::atomic strong_count; std::atomic weak_count; ControlBlock() : strong_count(1), weak_count(1) { } // Start with 1 strong, 1 weak (biased) /** * @brief Increment strong reference count * @return Previous strong count */ uint32_t increment_strong() noexcept { return strong_count.fetch_add(1, std::memory_order_relaxed); } /** * @brief Decrement strong reference count * @return Previous strong count */ uint32_t decrement_strong() noexcept { return strong_count.fetch_sub(1, std::memory_order_acq_rel); } /** * @brief Increment weak reference count * @return Previous weak count */ uint32_t increment_weak() noexcept { return weak_count.fetch_add(1, std::memory_order_relaxed); } /** * @brief Decrement weak reference count * @return Previous weak count */ uint32_t decrement_weak() noexcept { return weak_count.fetch_sub(1, std::memory_order_acq_rel); } }; } // namespace detail /** * @brief Strong reference to a shared object (similar to std::shared_ptr) * * Ref manages shared ownership of an object. The object is automatically * destroyed when the last Ref pointing to it is destroyed. * * Usage: * - Use make_ref() to create new objects * - Copy/assign to share ownership * - Use get(), operator*, operator-> to access the object * - Use operator bool() to check if valid * - Use reset() to release ownership * * Limitations compared to std::shared_ptr: * - Cannot take ownership of raw pointers * - Objects can only be created via make_ref() for proper memory layout * - No custom deleter support * - No enable_shared_from_this / shared_from_this() integration * - No aliasing constructor (sharing ownership with different pointer) * - No array support (Ref) * - No atomic operations (atomic_load, atomic_store, etc.) * * Thread safety: All operations are thread-safe. The managed object * itself is NOT automatically thread-safe. */ template struct Ref { /** * @brief Get raw pointer to managed object */ T *get() const noexcept { return ptr; } /** * @brief Dereference operator */ T &operator*() const { return *ptr; } /** * @brief Arrow operator */ T *operator->() const { return ptr; } /** * @brief Check if Ref is valid (not empty) */ explicit operator bool() const noexcept { return ptr != nullptr; } /** * @brief Destructor - decrements strong reference count */ ~Ref() { release(); } /** * @brief Copy constructor - increments strong reference count */ Ref(const Ref &other) noexcept : ptr(other.ptr), control_block(other.control_block) { if (control_block) { control_block->increment_strong(); } } /** * @brief Converting copy constructor for polymorphism (Derived -> Base) */ template Ref(const Ref &other) noexcept requires std::is_convertible_v : ptr(other.ptr), control_block(other.control_block) { if (control_block) { control_block->increment_strong(); } } /** * @brief Copy assignment operator */ Ref &operator=(const Ref &other) noexcept { if (this != &other) { release(); ptr = other.ptr; control_block = other.control_block; if (control_block) { control_block->increment_strong(); } } return *this; } /** * @brief Converting assignment operator for polymorphism (Derived -> Base) */ template Ref &operator=(const Ref &other) noexcept requires std::is_convertible_v { release(); ptr = other.ptr; control_block = other.control_block; if (control_block) { control_block->increment_strong(); } return *this; } /** * @brief Move constructor - transfers ownership */ Ref(Ref &&other) noexcept : ptr(other.ptr), control_block(other.control_block) { other.ptr = nullptr; other.control_block = nullptr; } /** * @brief Converting move constructor for polymorphism (Derived -> Base) */ template Ref(Ref &&other) noexcept requires std::is_convertible_v : ptr(other.ptr), control_block(other.control_block) { other.ptr = nullptr; other.control_block = nullptr; } /** * @brief Move assignment operator */ Ref &operator=(Ref &&other) noexcept { if (this != &other) { release(); ptr = other.ptr; control_block = other.control_block; other.ptr = nullptr; other.control_block = nullptr; } return *this; } /** * @brief Converting move assignment operator for polymorphism (Derived -> * Base) */ template Ref &operator=(Ref &&other) noexcept requires std::is_convertible_v { release(); ptr = other.ptr; control_block = other.control_block; other.ptr = nullptr; other.control_block = nullptr; return *this; } /** * @brief Reset to empty state */ void reset() noexcept { release(); ptr = nullptr; control_block = nullptr; } /** * @brief Equality comparison */ bool operator==(const Ref &other) const noexcept { return control_block == other.control_block; } /** * @brief Inequality comparison */ bool operator!=(const Ref &other) const noexcept { return !(*this == other); } /** * @brief Default constructor - creates empty Ref */ Ref() : ptr(nullptr), control_block(nullptr) {} private: explicit Ref(T *object_ptr, detail::ControlBlock *cb) : ptr(object_ptr), control_block(cb) {} T *ptr; detail::ControlBlock *control_block; /** * @brief Release current reference and handle cleanup */ void release() noexcept { if (control_block) { uint32_t prev_strong = control_block->decrement_strong(); // If this was the last strong reference, destroy the object if (prev_strong == 1) { // We need to call the destructor before we decrement the weak count, to // account for the possibility that T has a WeakRef to itself. ptr->~T(); // Release the bias - decrement weak count for strong references uint32_t prev_weak = control_block->decrement_weak(); // If weak count hits 0, destroy and free control block if (prev_weak == 1) { control_block->~ControlBlock(); std::free(control_block); } } } } template friend Ref make_ref(Args &&...args); template friend struct WeakRef; template friend struct Ref; }; /** * @brief Weak reference to a shared object (similar to std::weak_ptr) * * WeakRef holds a non-owning reference to an object managed by Ref. * It can be used to break circular dependencies and safely observe objects * that might be destroyed by other threads. * * Usage: * - Create from Ref to observe without owning * - Use lock() to attempt promotion to Ref * - Returns empty Ref if object was already destroyed * - Use reset() to stop observing * * Self-referencing pattern: Objects can safely contain WeakRef members * pointing to themselves. The implementation ensures proper destruction * order to prevent use-after-free when the object destructor runs. * * Thread safety: All operations are thread-safe. The observed object * may be destroyed by other threads at any time. */ template struct WeakRef { /** * @brief Attempt to promote WeakRef to Ref * @return Valid Ref if object still alive, empty Ref otherwise */ Ref lock() const { if (!control_block) { return Ref(); } // Try to increment strong count if it's not zero uint32_t expected_strong = control_block->strong_count.load(std::memory_order_relaxed); while (expected_strong > 0) { // Try to increment the strong count if (control_block->strong_count.compare_exchange_weak( expected_strong, expected_strong + 1, std::memory_order_acquire, std::memory_order_relaxed)) { // Success - we incremented the strong count return Ref(get_object_ptr(), control_block); } // CAS failed, expected_strong now contains the current value, retry } // Strong count was 0, object is being destroyed return Ref(); } /** * @brief Destructor - decrements weak reference count */ ~WeakRef() { release(); } /** * @brief Copy constructor from WeakRef */ WeakRef(const WeakRef &other) noexcept : control_block(other.control_block) { if (control_block) { control_block->increment_weak(); } } /** * @brief Copy constructor from Ref */ WeakRef(const Ref &ref) noexcept : control_block(ref.control_block) { if (control_block) { control_block->increment_weak(); } } /** * @brief Converting copy constructor from WeakRef for polymorphism */ template WeakRef(const WeakRef &other) noexcept requires std::is_convertible_v : control_block(other.control_block) { if (control_block) { control_block->increment_weak(); } } /** * @brief Converting copy constructor from Ref for polymorphism */ template WeakRef(const Ref &ref) noexcept requires std::is_convertible_v : control_block(ref.control_block) { if (control_block) { control_block->increment_weak(); } } /** * @brief Converting copy assignment from WeakRef for polymorphism */ template WeakRef &operator=(const WeakRef &other) noexcept requires std::is_convertible_v { release(); control_block = other.control_block; if (control_block) { control_block->increment_weak(); } return *this; } /** * @brief Converting copy assignment from Ref for polymorphism */ template WeakRef &operator=(const Ref &ref) noexcept requires std::is_convertible_v { release(); control_block = ref.control_block; if (control_block) { control_block->increment_weak(); } return *this; } /** * @brief Converting move constructor from WeakRef for polymorphism */ template WeakRef(WeakRef &&other) noexcept requires std::is_convertible_v : control_block(other.control_block) { other.control_block = nullptr; } /** * @brief Converting move assignment from WeakRef for polymorphism */ template WeakRef &operator=(WeakRef &&other) noexcept requires std::is_convertible_v { release(); control_block = other.control_block; other.control_block = nullptr; return *this; } /** * @brief Copy assignment from WeakRef */ WeakRef &operator=(const WeakRef &other) noexcept { if (this != &other) { release(); control_block = other.control_block; if (control_block) { control_block->increment_weak(); } } return *this; } /** * @brief Copy assignment from Ref */ WeakRef &operator=(const Ref &ref) noexcept { release(); control_block = ref.control_block; if (control_block) { control_block->increment_weak(); } return *this; } /** * @brief Move constructor */ WeakRef(WeakRef &&other) noexcept : control_block(other.control_block) { other.control_block = nullptr; } /** * @brief Move assignment */ WeakRef &operator=(WeakRef &&other) noexcept { if (this != &other) { release(); control_block = other.control_block; other.control_block = nullptr; } return *this; } /** * @brief Reset to empty state */ void reset() noexcept { release(); control_block = nullptr; } /** * @brief Default constructor - creates empty WeakRef */ WeakRef() : control_block(nullptr) {} private: explicit WeakRef(detail::ControlBlock *cb) : control_block(cb) {} detail::ControlBlock *control_block; // Helper to calculate object pointer from control block T *get_object_ptr() const { 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); } /** * @brief Release current weak reference and handle cleanup */ void release() noexcept { if (control_block) { uint32_t prev_weak = control_block->decrement_weak(); // If weak count hits 0, destroy and free control block if (prev_weak == 1) { control_block->~ControlBlock(); std::free(control_block); } } } template friend struct Ref; template friend struct WeakRef; }; /** * @brief Create a new managed object wrapped in Ref * * This is the only way to create Ref objects. It performs a single * allocation for both the control block and object, improving cache locality. * * @tparam T Type of object to create * @tparam Args Types of constructor arguments * @param args Arguments forwarded to T's constructor * @return Ref managing the newly created object * * Example: * @code * auto obj = make_ref(arg1, arg2); * auto empty_vec = make_ref>(); * @endcode * * Thread safety: Safe to call from multiple threads simultaneously. */ 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); constexpr size_t total_alignment = std::max(alignof(detail::ControlBlock), alignment); constexpr size_t total_size = padded_cb_size + sizeof(T); constexpr size_t aligned_total_size = (total_size + total_alignment - 1) & ~(total_alignment - 1); char *buf = reinterpret_cast( std::aligned_alloc(total_alignment, aligned_total_size)); if (!buf) { std::fprintf(stderr, "Out of memory\n"); std::abort(); } auto *cb = new (buf) detail::ControlBlock(); T *obj = new (buf + padded_cb_size) T{std::forward(args)...}; return Ref(obj, cb); }