Require explicit copies for Ref/WeakRef
This commit is contained in:
@@ -27,6 +27,18 @@ template <typename T> struct PointerTraits<std::shared_ptr<T>> {
|
|||||||
return std::make_shared<T>(std::forward<Args>(args)...);
|
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pointer_type copy(const pointer_type &ptr) {
|
||||||
|
return ptr; // std::shared_ptr copies implicitly
|
||||||
|
}
|
||||||
|
|
||||||
|
static weak_type as_weak(const pointer_type &ptr) {
|
||||||
|
return ptr; // std::weak_ptr converts implicitly from std::shared_ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
static weak_type copy_weak(const weak_type &weak) {
|
||||||
|
return weak; // std::weak_ptr copies implicitly
|
||||||
|
}
|
||||||
|
|
||||||
static const char *name() { return "std::shared_ptr"; }
|
static const char *name() { return "std::shared_ptr"; }
|
||||||
static const char *weak_name() { return "std::weak_ptr"; }
|
static const char *weak_name() { return "std::weak_ptr"; }
|
||||||
};
|
};
|
||||||
@@ -39,6 +51,18 @@ template <typename T> struct PointerTraits<Ref<T>> {
|
|||||||
return make_ref<T>(std::forward<Args>(args)...);
|
return make_ref<T>(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pointer_type copy(const pointer_type &ptr) {
|
||||||
|
return ptr.copy(); // Ref requires explicit copy
|
||||||
|
}
|
||||||
|
|
||||||
|
static weak_type as_weak(const pointer_type &ptr) {
|
||||||
|
return ptr.as_weak(); // Ref requires explicit as_weak
|
||||||
|
}
|
||||||
|
|
||||||
|
static weak_type copy_weak(const weak_type &weak) {
|
||||||
|
return weak.copy(); // WeakRef requires explicit copy
|
||||||
|
}
|
||||||
|
|
||||||
static const char *name() { return "Ref"; }
|
static const char *name() { return "Ref"; }
|
||||||
static const char *weak_name() { return "WeakRef"; }
|
static const char *weak_name() { return "WeakRef"; }
|
||||||
};
|
};
|
||||||
@@ -67,7 +91,7 @@ void benchmark_copy(ankerl::nanobench::Bench &bench) {
|
|||||||
|
|
||||||
auto original = Traits::make(TestObject{123});
|
auto original = Traits::make(TestObject{123});
|
||||||
bench.run(std::string(Traits::name()) + " copy", [&] {
|
bench.run(std::string(Traits::name()) + " copy", [&] {
|
||||||
auto copy = original;
|
auto copy = Traits::copy(original);
|
||||||
ankerl::nanobench::doNotOptimizeAway(copy);
|
ankerl::nanobench::doNotOptimizeAway(copy);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -91,9 +115,9 @@ void benchmark_weak_copy(ankerl::nanobench::Bench &bench) {
|
|||||||
force_multithreaded();
|
force_multithreaded();
|
||||||
|
|
||||||
auto strong_ptr = Traits::make(TestObject{123});
|
auto strong_ptr = Traits::make(TestObject{123});
|
||||||
typename Traits::weak_type weak_original = strong_ptr;
|
typename Traits::weak_type weak_original = Traits::as_weak(strong_ptr);
|
||||||
bench.run(std::string(Traits::weak_name()) + " copy", [&] {
|
bench.run(std::string(Traits::weak_name()) + " copy", [&] {
|
||||||
auto weak_copy = weak_original;
|
auto weak_copy = Traits::copy_weak(weak_original);
|
||||||
ankerl::nanobench::doNotOptimizeAway(weak_copy);
|
ankerl::nanobench::doNotOptimizeAway(weak_copy);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -103,7 +127,7 @@ void benchmark_weak_move(ankerl::nanobench::Bench &bench) {
|
|||||||
using Traits = PointerTraits<PtrType>;
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
auto strong_ptr = Traits::make(TestObject{123});
|
auto strong_ptr = Traits::make(TestObject{123});
|
||||||
typename Traits::weak_type weak_original = strong_ptr;
|
typename Traits::weak_type weak_original = Traits::as_weak(strong_ptr);
|
||||||
bench.run(std::string(Traits::weak_name()) + " move", [&] {
|
bench.run(std::string(Traits::weak_name()) + " move", [&] {
|
||||||
auto weak_moved = std::move(weak_original);
|
auto weak_moved = std::move(weak_original);
|
||||||
ankerl::nanobench::doNotOptimizeAway(weak_moved);
|
ankerl::nanobench::doNotOptimizeAway(weak_moved);
|
||||||
@@ -126,7 +150,7 @@ void benchmark_weak_lock_success(ankerl::nanobench::Bench &bench) {
|
|||||||
using Traits = PointerTraits<PtrType>;
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
auto strong_ptr = Traits::make(TestObject{789});
|
auto strong_ptr = Traits::make(TestObject{789});
|
||||||
typename Traits::weak_type weak_ptr = strong_ptr;
|
typename Traits::weak_type weak_ptr = Traits::as_weak(strong_ptr);
|
||||||
bench.run(std::string(Traits::weak_name()) + " lock success", [&] {
|
bench.run(std::string(Traits::weak_name()) + " lock success", [&] {
|
||||||
auto locked = weak_ptr.lock();
|
auto locked = weak_ptr.lock();
|
||||||
ankerl::nanobench::doNotOptimizeAway(locked);
|
ankerl::nanobench::doNotOptimizeAway(locked);
|
||||||
@@ -140,7 +164,7 @@ void benchmark_weak_lock_failure(ankerl::nanobench::Bench &bench) {
|
|||||||
typename Traits::weak_type weak_ptr;
|
typename Traits::weak_type weak_ptr;
|
||||||
{
|
{
|
||||||
auto strong_ptr = Traits::make(TestObject{999});
|
auto strong_ptr = Traits::make(TestObject{999});
|
||||||
weak_ptr = strong_ptr;
|
weak_ptr = Traits::as_weak(strong_ptr);
|
||||||
}
|
}
|
||||||
bench.run(std::string(Traits::weak_name()) + " lock failure", [&] {
|
bench.run(std::string(Traits::weak_name()) + " lock failure", [&] {
|
||||||
auto locked = weak_ptr.lock();
|
auto locked = weak_ptr.lock();
|
||||||
@@ -163,7 +187,7 @@ void benchmark_multithreaded_copy(ankerl::nanobench::Bench &bench,
|
|||||||
for (int i = 0; i < num_threads - 1; ++i) {
|
for (int i = 0; i < num_threads - 1; ++i) {
|
||||||
background_threads.emplace_back([&]() {
|
background_threads.emplace_back([&]() {
|
||||||
while (keep_running.load(std::memory_order_relaxed)) {
|
while (keep_running.load(std::memory_order_relaxed)) {
|
||||||
auto copy = ptr;
|
auto copy = Traits::copy(ptr);
|
||||||
ankerl::nanobench::doNotOptimizeAway(copy);
|
ankerl::nanobench::doNotOptimizeAway(copy);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -171,7 +195,7 @@ void benchmark_multithreaded_copy(ankerl::nanobench::Bench &bench,
|
|||||||
|
|
||||||
// Benchmark the foreground thread under contention
|
// Benchmark the foreground thread under contention
|
||||||
bench.run(std::string(Traits::name()) + " copy under contention", [&] {
|
bench.run(std::string(Traits::name()) + " copy under contention", [&] {
|
||||||
auto copy = ptr;
|
auto copy = Traits::copy(ptr);
|
||||||
ankerl::nanobench::doNotOptimizeAway(copy);
|
ankerl::nanobench::doNotOptimizeAway(copy);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -189,7 +213,7 @@ void benchmark_multithreaded_weak_lock(ankerl::nanobench::Bench &bench,
|
|||||||
|
|
||||||
// Create the shared object and weak reference outside the benchmark
|
// Create the shared object and weak reference outside the benchmark
|
||||||
auto strong_ptr = Traits::make(TestObject{789});
|
auto strong_ptr = Traits::make(TestObject{789});
|
||||||
typename Traits::weak_type weak_ptr = strong_ptr;
|
typename Traits::weak_type weak_ptr = Traits::as_weak(strong_ptr);
|
||||||
|
|
||||||
// Create background threads that will create contention
|
// Create background threads that will create contention
|
||||||
std::atomic<bool> keep_running{true};
|
std::atomic<bool> keep_running{true};
|
||||||
@@ -224,7 +248,7 @@ void benchmark_weak_copy_with_strong_contention(ankerl::nanobench::Bench &bench,
|
|||||||
|
|
||||||
// Create the shared object and weak reference outside the benchmark
|
// Create the shared object and weak reference outside the benchmark
|
||||||
auto strong_ptr = Traits::make(TestObject{456});
|
auto strong_ptr = Traits::make(TestObject{456});
|
||||||
typename Traits::weak_type weak_ptr = strong_ptr;
|
typename Traits::weak_type weak_ptr = Traits::as_weak(strong_ptr);
|
||||||
|
|
||||||
// Create background threads copying the strong pointer
|
// Create background threads copying the strong pointer
|
||||||
std::atomic<bool> keep_running{true};
|
std::atomic<bool> keep_running{true};
|
||||||
@@ -233,7 +257,7 @@ void benchmark_weak_copy_with_strong_contention(ankerl::nanobench::Bench &bench,
|
|||||||
for (int i = 0; i < num_threads - 1; ++i) {
|
for (int i = 0; i < num_threads - 1; ++i) {
|
||||||
background_threads.emplace_back([&]() {
|
background_threads.emplace_back([&]() {
|
||||||
while (keep_running.load(std::memory_order_relaxed)) {
|
while (keep_running.load(std::memory_order_relaxed)) {
|
||||||
auto copy = strong_ptr;
|
auto copy = Traits::copy(strong_ptr);
|
||||||
ankerl::nanobench::doNotOptimizeAway(copy);
|
ankerl::nanobench::doNotOptimizeAway(copy);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -242,7 +266,7 @@ void benchmark_weak_copy_with_strong_contention(ankerl::nanobench::Bench &bench,
|
|||||||
// Benchmark weak reference copying under strong reference contention
|
// Benchmark weak reference copying under strong reference contention
|
||||||
bench.run(std::string(Traits::weak_name()) + " copy with strong contention",
|
bench.run(std::string(Traits::weak_name()) + " copy with strong contention",
|
||||||
[&] {
|
[&] {
|
||||||
auto weak_copy = weak_ptr;
|
auto weak_copy = Traits::copy_weak(weak_ptr);
|
||||||
ankerl::nanobench::doNotOptimizeAway(weak_copy);
|
ankerl::nanobench::doNotOptimizeAway(weak_copy);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -260,7 +284,7 @@ void benchmark_strong_copy_with_weak_contention(ankerl::nanobench::Bench &bench,
|
|||||||
|
|
||||||
// Create the shared object and weak reference outside the benchmark
|
// Create the shared object and weak reference outside the benchmark
|
||||||
auto strong_ptr = Traits::make(TestObject{789});
|
auto strong_ptr = Traits::make(TestObject{789});
|
||||||
typename Traits::weak_type weak_ptr = strong_ptr;
|
typename Traits::weak_type weak_ptr = Traits::as_weak(strong_ptr);
|
||||||
|
|
||||||
// Create background threads copying the weak pointer
|
// Create background threads copying the weak pointer
|
||||||
std::atomic<bool> keep_running{true};
|
std::atomic<bool> keep_running{true};
|
||||||
@@ -269,7 +293,7 @@ void benchmark_strong_copy_with_weak_contention(ankerl::nanobench::Bench &bench,
|
|||||||
for (int i = 0; i < num_threads - 1; ++i) {
|
for (int i = 0; i < num_threads - 1; ++i) {
|
||||||
background_threads.emplace_back([&]() {
|
background_threads.emplace_back([&]() {
|
||||||
while (keep_running.load(std::memory_order_relaxed)) {
|
while (keep_running.load(std::memory_order_relaxed)) {
|
||||||
auto weak_copy = weak_ptr;
|
auto weak_copy = Traits::copy_weak(weak_ptr);
|
||||||
ankerl::nanobench::doNotOptimizeAway(weak_copy);
|
ankerl::nanobench::doNotOptimizeAway(weak_copy);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -277,7 +301,7 @@ void benchmark_strong_copy_with_weak_contention(ankerl::nanobench::Bench &bench,
|
|||||||
|
|
||||||
// Benchmark strong reference copying under weak reference contention
|
// Benchmark strong reference copying under weak reference contention
|
||||||
bench.run(std::string(Traits::name()) + " copy with weak contention", [&] {
|
bench.run(std::string(Traits::name()) + " copy with weak contention", [&] {
|
||||||
auto strong_copy = strong_ptr;
|
auto strong_copy = Traits::copy(strong_ptr);
|
||||||
ankerl::nanobench::doNotOptimizeAway(strong_copy);
|
ankerl::nanobench::doNotOptimizeAway(strong_copy);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
* Basic usage:
|
* Basic usage:
|
||||||
* @code
|
* @code
|
||||||
* auto obj = make_ref<MyClass>(args...); // Create managed object
|
* auto obj = make_ref<MyClass>(args...); // Create managed object
|
||||||
* auto copy = obj; // Copy (thread-safe)
|
* auto copy = obj.copy(); // Explicit copy (thread-safe)
|
||||||
* WeakRef<MyClass> weak = obj; // Create weak reference
|
* WeakRef<MyClass> weak = obj.as_weak(); // Create weak reference
|
||||||
* auto locked = weak.lock(); // Try to promote to strong
|
* auto locked = weak.lock(); // Try to promote to strong
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
@@ -31,6 +31,9 @@
|
|||||||
* safely copy, move, and destroy references to the same object.
|
* safely copy, move, and destroy references to the same object.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
template <typename T> struct WeakRef;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
struct ControlBlock {
|
struct ControlBlock {
|
||||||
std::atomic<uint32_t> strong_count;
|
std::atomic<uint32_t> strong_count;
|
||||||
@@ -126,57 +129,28 @@ template <typename T> struct Ref {
|
|||||||
~Ref() { release(); }
|
~Ref() { release(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy constructor - increments strong reference count
|
* @brief Copy constructor - deleted to prevent accidental copies
|
||||||
|
* Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
Ref(const Ref &other) noexcept
|
Ref(const Ref &other) = delete;
|
||||||
: ptr(other.ptr), control_block(other.control_block) {
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_strong();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converting copy constructor for polymorphism (Derived -> Base)
|
* @brief Converting copy constructor - deleted to prevent accidental copies
|
||||||
|
* Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
template <typename U>
|
template <typename U> Ref(const Ref<U> &other) = delete;
|
||||||
Ref(const Ref<U> &other) noexcept
|
|
||||||
requires std::is_convertible_v<U *, T *>
|
|
||||||
: ptr(other.ptr), control_block(other.control_block) {
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_strong();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy assignment operator
|
* @brief Copy assignment operator - deleted to prevent accidental copies
|
||||||
|
* Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
Ref &operator=(const Ref &other) noexcept {
|
Ref &operator=(const Ref &other) = delete;
|
||||||
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)
|
* @brief Converting assignment operator - deleted to prevent accidental
|
||||||
|
* copies Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
template <typename U>
|
template <typename U> Ref &operator=(const Ref<U> &other) = delete;
|
||||||
Ref &operator=(const Ref<U> &other) noexcept
|
|
||||||
requires std::is_convertible_v<U *, T *>
|
|
||||||
{
|
|
||||||
release();
|
|
||||||
ptr = other.ptr;
|
|
||||||
control_block = other.control_block;
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_strong();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move constructor - transfers ownership
|
* @brief Move constructor - transfers ownership
|
||||||
@@ -228,6 +202,28 @@ template <typename T> struct Ref {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Explicitly create a copy with shared ownership
|
||||||
|
* @return New Ref that shares ownership of the same object
|
||||||
|
*/
|
||||||
|
[[nodiscard]] Ref copy() const noexcept {
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_strong();
|
||||||
|
}
|
||||||
|
return Ref(ptr, control_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a WeakRef that observes this object
|
||||||
|
* @return New WeakRef that observes the same object
|
||||||
|
*/
|
||||||
|
[[nodiscard]] WeakRef<T> as_weak() const noexcept {
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_weak();
|
||||||
|
}
|
||||||
|
return WeakRef<T>(control_block);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reset to empty state
|
* @brief Reset to empty state
|
||||||
*/
|
*/
|
||||||
@@ -347,76 +343,40 @@ template <typename T> struct WeakRef {
|
|||||||
~WeakRef() { release(); }
|
~WeakRef() { release(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy constructor from WeakRef
|
* @brief Copy constructor from WeakRef - deleted to prevent accidental copies
|
||||||
|
* Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
WeakRef(const WeakRef &other) noexcept : control_block(other.control_block) {
|
WeakRef(const WeakRef &other) = delete;
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_weak();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy constructor from Ref
|
* @brief Copy constructor from Ref - deleted to prevent accidental copies
|
||||||
|
* Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
WeakRef(const Ref<T> &ref) noexcept : control_block(ref.control_block) {
|
WeakRef(const Ref<T> &ref) = delete;
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_weak();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converting copy constructor from WeakRef for polymorphism
|
* @brief Converting copy constructor from WeakRef - deleted to prevent
|
||||||
|
* accidental copies Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
template <typename U>
|
template <typename U> WeakRef(const WeakRef<U> &other) = delete;
|
||||||
WeakRef(const WeakRef<U> &other) noexcept
|
|
||||||
requires std::is_convertible_v<U *, T *>
|
|
||||||
: control_block(other.control_block) {
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_weak();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converting copy constructor from Ref for polymorphism
|
* @brief Converting copy constructor from Ref - deleted to prevent accidental
|
||||||
|
* copies Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
template <typename U>
|
template <typename U> WeakRef(const Ref<U> &ref) = delete;
|
||||||
WeakRef(const Ref<U> &ref) noexcept
|
|
||||||
requires std::is_convertible_v<U *, T *>
|
|
||||||
: control_block(ref.control_block) {
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_weak();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converting copy assignment from WeakRef for polymorphism
|
* @brief Converting copy assignment from WeakRef - deleted to prevent
|
||||||
|
* accidental copies Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
template <typename U>
|
template <typename U> WeakRef &operator=(const WeakRef<U> &other) = delete;
|
||||||
WeakRef &operator=(const WeakRef<U> &other) noexcept
|
|
||||||
requires std::is_convertible_v<U *, T *>
|
|
||||||
{
|
|
||||||
release();
|
|
||||||
control_block = other.control_block;
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_weak();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converting copy assignment from Ref for polymorphism
|
* @brief Converting copy assignment from Ref - deleted to prevent accidental
|
||||||
|
* copies Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
template <typename U>
|
template <typename U> WeakRef &operator=(const Ref<U> &ref) = delete;
|
||||||
WeakRef &operator=(const Ref<U> &ref) noexcept
|
|
||||||
requires std::is_convertible_v<U *, T *>
|
|
||||||
{
|
|
||||||
release();
|
|
||||||
control_block = ref.control_block;
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_weak();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converting move constructor from WeakRef for polymorphism
|
* @brief Converting move constructor from WeakRef for polymorphism
|
||||||
@@ -442,30 +402,16 @@ template <typename T> struct WeakRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy assignment from WeakRef
|
* @brief Copy assignment from WeakRef - deleted to prevent accidental copies
|
||||||
|
* Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
WeakRef &operator=(const WeakRef &other) noexcept {
|
WeakRef &operator=(const WeakRef &other) = delete;
|
||||||
if (this != &other) {
|
|
||||||
release();
|
|
||||||
control_block = other.control_block;
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_weak();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy assignment from Ref
|
* @brief Copy assignment from Ref - deleted to prevent accidental copies
|
||||||
|
* Use copy() method for explicit copying
|
||||||
*/
|
*/
|
||||||
WeakRef &operator=(const Ref<T> &ref) noexcept {
|
WeakRef &operator=(const Ref<T> &ref) = delete;
|
||||||
release();
|
|
||||||
control_block = ref.control_block;
|
|
||||||
if (control_block) {
|
|
||||||
control_block->increment_weak();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move constructor
|
* @brief Move constructor
|
||||||
@@ -486,6 +432,17 @@ template <typename T> struct WeakRef {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Explicitly create a copy with shared weak reference
|
||||||
|
* @return New WeakRef that observes the same object
|
||||||
|
*/
|
||||||
|
[[nodiscard]] WeakRef copy() const noexcept {
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_weak();
|
||||||
|
}
|
||||||
|
return WeakRef(control_block);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reset to empty state
|
* @brief Reset to empty state
|
||||||
*/
|
*/
|
||||||
@@ -550,6 +507,8 @@ private:
|
|||||||
* @code
|
* @code
|
||||||
* auto obj = make_ref<MyClass>(arg1, arg2);
|
* auto obj = make_ref<MyClass>(arg1, arg2);
|
||||||
* auto empty_vec = make_ref<std::vector<int>>();
|
* auto empty_vec = make_ref<std::vector<int>>();
|
||||||
|
* auto obj_copy = obj.copy(); // Explicit copy
|
||||||
|
* WeakRef<MyClass> weak = obj.as_weak(); // Create weak reference
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
* Thread safety: Safe to call from multiple threads simultaneously.
|
* Thread safety: Safe to call from multiple threads simultaneously.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Ref<Server> Server::create(const weaseldb::Config &config,
|
|||||||
ConnectionHandler &handler,
|
ConnectionHandler &handler,
|
||||||
const std::vector<int> &listen_fds) {
|
const std::vector<int> &listen_fds) {
|
||||||
auto result = make_ref<Server>(config, handler, listen_fds);
|
auto result = make_ref<Server>(config, handler, listen_fds);
|
||||||
result->self_ = result;
|
result->self_ = result.as_weak();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ int Server::create_local_connection() {
|
|||||||
// Create Connection object
|
// Create Connection object
|
||||||
auto connection = std::unique_ptr<Connection>(new Connection(
|
auto connection = std::unique_ptr<Connection>(new Connection(
|
||||||
addr, server_fd, connection_id_.fetch_add(1, std::memory_order_relaxed),
|
addr, server_fd, connection_id_.fetch_add(1, std::memory_order_relaxed),
|
||||||
epoll_index, &handler_, self_));
|
epoll_index, &handler_, self_.copy()));
|
||||||
|
|
||||||
// Store in registry
|
// Store in registry
|
||||||
connection_registry_.store(server_fd, std::move(connection));
|
connection_registry_.store(server_fd, std::move(connection));
|
||||||
@@ -422,7 +422,7 @@ void Server::start_io_threads(std::vector<std::thread> &threads) {
|
|||||||
batch[batch_count] = std::unique_ptr<Connection>(new Connection(
|
batch[batch_count] = std::unique_ptr<Connection>(new Connection(
|
||||||
addr, fd,
|
addr, fd,
|
||||||
connection_id_.fetch_add(1, std::memory_order_relaxed),
|
connection_id_.fetch_add(1, std::memory_order_relaxed),
|
||||||
epoll_index, &handler_, self_));
|
epoll_index, &handler_, self_.copy()));
|
||||||
batch_events[batch_count] =
|
batch_events[batch_count] =
|
||||||
EPOLLIN; // New connections always start with read
|
EPOLLIN; // New connections always start with read
|
||||||
batch_count++;
|
batch_count++;
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ TEST_CASE("Ref basic functionality") {
|
|||||||
CHECK((*ref).value == 42);
|
CHECK((*ref).value == 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("copy construction increments reference count") {
|
SUBCASE("explicit copy increments reference count") {
|
||||||
auto ref1 = make_ref<TestObject>(123);
|
auto ref1 = make_ref<TestObject>(123);
|
||||||
auto ref2 = ref1;
|
auto ref2 = ref1.copy();
|
||||||
|
|
||||||
CHECK(ref1);
|
CHECK(ref1);
|
||||||
CHECK(ref2);
|
CHECK(ref2);
|
||||||
@@ -63,11 +63,11 @@ TEST_CASE("Ref basic functionality") {
|
|||||||
CHECK(ref2->value == 123);
|
CHECK(ref2->value == 123);
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("copy assignment works correctly") {
|
SUBCASE("explicit copy assignment works correctly") {
|
||||||
auto ref1 = make_ref<TestObject>(100);
|
auto ref1 = make_ref<TestObject>(100);
|
||||||
auto ref2 = make_ref<TestObject>(200);
|
auto ref2 = make_ref<TestObject>(200);
|
||||||
|
|
||||||
ref2 = ref1;
|
ref2 = ref1.copy();
|
||||||
CHECK(ref1.get() == ref2.get());
|
CHECK(ref1.get() == ref2.get());
|
||||||
CHECK(ref1->value == 100);
|
CHECK(ref1->value == 100);
|
||||||
CHECK(ref2->value == 100);
|
CHECK(ref2->value == 100);
|
||||||
@@ -109,7 +109,7 @@ TEST_CASE("Ref basic functionality") {
|
|||||||
TEST_CASE("WeakRef basic functionality") {
|
TEST_CASE("WeakRef basic functionality") {
|
||||||
SUBCASE("construction from Ref") {
|
SUBCASE("construction from Ref") {
|
||||||
auto ref = make_ref<TestObject>(333);
|
auto ref = make_ref<TestObject>(333);
|
||||||
WeakRef<TestObject> weak_ref = ref;
|
WeakRef<TestObject> weak_ref = ref.as_weak();
|
||||||
|
|
||||||
auto locked = weak_ref.lock();
|
auto locked = weak_ref.lock();
|
||||||
CHECK(locked);
|
CHECK(locked);
|
||||||
@@ -121,7 +121,7 @@ TEST_CASE("WeakRef basic functionality") {
|
|||||||
WeakRef<TestObject> weak_ref;
|
WeakRef<TestObject> weak_ref;
|
||||||
{
|
{
|
||||||
auto ref = make_ref<TestObject>(444);
|
auto ref = make_ref<TestObject>(444);
|
||||||
weak_ref = ref;
|
weak_ref = ref.as_weak();
|
||||||
}
|
}
|
||||||
// ref goes out of scope, object should be destroyed
|
// ref goes out of scope, object should be destroyed
|
||||||
|
|
||||||
@@ -131,8 +131,8 @@ TEST_CASE("WeakRef basic functionality") {
|
|||||||
|
|
||||||
SUBCASE("copy and move semantics") {
|
SUBCASE("copy and move semantics") {
|
||||||
auto ref = make_ref<TestObject>(666);
|
auto ref = make_ref<TestObject>(666);
|
||||||
WeakRef<TestObject> weak1 = ref;
|
WeakRef<TestObject> weak1 = ref.as_weak();
|
||||||
WeakRef<TestObject> weak2 = weak1; // copy
|
WeakRef<TestObject> weak2 = weak1.copy(); // explicit copy
|
||||||
WeakRef<TestObject> weak3 = std::move(weak1); // move
|
WeakRef<TestObject> weak3 = std::move(weak1); // move
|
||||||
|
|
||||||
auto locked2 = weak2.lock();
|
auto locked2 = weak2.lock();
|
||||||
@@ -160,7 +160,7 @@ TEST_CASE("Ref thread safety") {
|
|||||||
start_latch.arrive_and_wait();
|
start_latch.arrive_and_wait();
|
||||||
|
|
||||||
for (int j = 0; j < copies_per_thread; ++j) {
|
for (int j = 0; j < copies_per_thread; ++j) {
|
||||||
auto copy = ref;
|
auto copy = ref.copy();
|
||||||
CHECK(copy);
|
CHECK(copy);
|
||||||
CHECK(copy->value == 777);
|
CHECK(copy->value == 777);
|
||||||
}
|
}
|
||||||
@@ -191,7 +191,7 @@ TEST_CASE("Control block cleanup race condition test") {
|
|||||||
WeakRef<TestObject> ptr2;
|
WeakRef<TestObject> ptr2;
|
||||||
auto setup = [&]() {
|
auto setup = [&]() {
|
||||||
ptr1 = make_ref<TestObject>(0);
|
ptr1 = make_ref<TestObject>(0);
|
||||||
ptr2 = ptr1;
|
ptr2 = ptr1.as_weak();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Barrier for synchronization - 2 participants (main thread + worker thread)
|
// Barrier for synchronization - 2 participants (main thread + worker thread)
|
||||||
@@ -243,7 +243,7 @@ TEST_CASE("WeakRef prevents circular references") {
|
|||||||
// Create object and weak reference
|
// Create object and weak reference
|
||||||
{
|
{
|
||||||
auto ref = make_ref<TestObject>(123);
|
auto ref = make_ref<TestObject>(123);
|
||||||
weak_ref = ref;
|
weak_ref = ref.as_weak();
|
||||||
|
|
||||||
// Should be able to lock while object exists
|
// Should be able to lock while object exists
|
||||||
auto locked = weak_ref.lock();
|
auto locked = weak_ref.lock();
|
||||||
@@ -262,8 +262,8 @@ TEST_CASE("WeakRef prevents circular references") {
|
|||||||
auto child = make_ref<Node>(2);
|
auto child = make_ref<Node>(2);
|
||||||
|
|
||||||
// Create potential cycle
|
// Create potential cycle
|
||||||
parent->next = child; // Strong reference: parent → child
|
parent->next = child.copy(); // Strong reference: parent → child
|
||||||
child->parent = parent; // WeakRef: child ⇝ parent (breaks cycle)
|
child->parent = parent.as_weak(); // WeakRef: child ⇝ parent (breaks cycle)
|
||||||
|
|
||||||
CHECK(parent->data == 1);
|
CHECK(parent->data == 1);
|
||||||
CHECK(child->data == 2);
|
CHECK(child->data == 2);
|
||||||
@@ -286,7 +286,7 @@ TEST_CASE("Polymorphic Ref conversions") {
|
|||||||
CHECK(derived_ref->get_value() == 30); // 10 + 20
|
CHECK(derived_ref->get_value() == 30); // 10 + 20
|
||||||
|
|
||||||
// Convert Ref<Derived> to Ref<Base>
|
// Convert Ref<Derived> to Ref<Base>
|
||||||
Ref<Base> base_ref = derived_ref;
|
Ref<Base> base_ref = derived_ref.copy();
|
||||||
CHECK(base_ref);
|
CHECK(base_ref);
|
||||||
CHECK(base_ref->get_value() == 30); // Virtual dispatch works
|
CHECK(base_ref->get_value() == 30); // Virtual dispatch works
|
||||||
CHECK(base_ref->base_value == 10);
|
CHECK(base_ref->base_value == 10);
|
||||||
@@ -303,7 +303,7 @@ TEST_CASE("Polymorphic Ref conversions") {
|
|||||||
CHECK(base_ref->get_value() == 100);
|
CHECK(base_ref->get_value() == 100);
|
||||||
|
|
||||||
// Assign derived to base
|
// Assign derived to base
|
||||||
base_ref = derived_ref;
|
base_ref = derived_ref.copy();
|
||||||
CHECK(base_ref->get_value() == 20); // 5 + 15
|
CHECK(base_ref->get_value() == 20); // 5 + 15
|
||||||
CHECK(base_ref.get() == derived_ref.get());
|
CHECK(base_ref.get() == derived_ref.get());
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ TEST_CASE("Polymorphic Ref conversions") {
|
|||||||
CHECK(another_derived->get_value() == 24); // 6 * 4
|
CHECK(another_derived->get_value() == 24); // 6 * 4
|
||||||
|
|
||||||
// Convert to base
|
// Convert to base
|
||||||
Ref<Base> base_ref = another_derived;
|
Ref<Base> base_ref = another_derived.copy();
|
||||||
CHECK(base_ref->get_value() == 24); // Virtual dispatch
|
CHECK(base_ref->get_value() == 24); // Virtual dispatch
|
||||||
CHECK(base_ref.get() == another_derived.get());
|
CHECK(base_ref.get() == another_derived.get());
|
||||||
}
|
}
|
||||||
@@ -349,10 +349,10 @@ TEST_CASE("Polymorphic WeakRef conversions") {
|
|||||||
auto derived_ref = make_ref<Derived>(3, 7);
|
auto derived_ref = make_ref<Derived>(3, 7);
|
||||||
|
|
||||||
// Create WeakRef<Derived>
|
// Create WeakRef<Derived>
|
||||||
WeakRef<Derived> weak_derived = derived_ref;
|
WeakRef<Derived> weak_derived = derived_ref.as_weak();
|
||||||
|
|
||||||
// Convert to WeakRef<Base>
|
// Convert to WeakRef<Base>
|
||||||
WeakRef<Base> weak_base = weak_derived;
|
WeakRef<Base> weak_base = weak_derived.copy();
|
||||||
|
|
||||||
// Both should lock to same object
|
// Both should lock to same object
|
||||||
auto locked_derived = weak_derived.lock();
|
auto locked_derived = weak_derived.lock();
|
||||||
@@ -368,11 +368,11 @@ TEST_CASE("Polymorphic WeakRef conversions") {
|
|||||||
auto derived_ref = make_ref<Derived>(4, 6);
|
auto derived_ref = make_ref<Derived>(4, 6);
|
||||||
auto base_ref = make_ref<Base>(999);
|
auto base_ref = make_ref<Base>(999);
|
||||||
|
|
||||||
WeakRef<Derived> weak_derived = derived_ref;
|
WeakRef<Derived> weak_derived = derived_ref.as_weak();
|
||||||
WeakRef<Base> weak_base = base_ref;
|
WeakRef<Base> weak_base = base_ref.as_weak();
|
||||||
|
|
||||||
// Assign derived weak ref to base weak ref
|
// Assign derived weak ref to base weak ref
|
||||||
weak_base = weak_derived;
|
weak_base = weak_derived.copy();
|
||||||
|
|
||||||
auto locked = weak_base.lock();
|
auto locked = weak_base.lock();
|
||||||
CHECK(locked);
|
CHECK(locked);
|
||||||
@@ -384,7 +384,7 @@ TEST_CASE("Polymorphic WeakRef conversions") {
|
|||||||
auto derived_ref = make_ref<Derived>(2, 8);
|
auto derived_ref = make_ref<Derived>(2, 8);
|
||||||
|
|
||||||
// Create WeakRef<Base> directly from Ref<Derived>
|
// Create WeakRef<Base> directly from Ref<Derived>
|
||||||
WeakRef<Base> weak_base = derived_ref;
|
WeakRef<Base> weak_base = derived_ref.as_weak();
|
||||||
|
|
||||||
auto locked = weak_base.lock();
|
auto locked = weak_base.lock();
|
||||||
CHECK(locked);
|
CHECK(locked);
|
||||||
@@ -394,7 +394,7 @@ TEST_CASE("Polymorphic WeakRef conversions") {
|
|||||||
|
|
||||||
SUBCASE("WeakRef move operations") {
|
SUBCASE("WeakRef move operations") {
|
||||||
auto derived_ref = make_ref<Derived>(1, 9);
|
auto derived_ref = make_ref<Derived>(1, 9);
|
||||||
WeakRef<Derived> weak_derived = derived_ref;
|
WeakRef<Derived> weak_derived = derived_ref.as_weak();
|
||||||
|
|
||||||
// Move construct
|
// Move construct
|
||||||
WeakRef<Base> weak_base = std::move(weak_derived);
|
WeakRef<Base> weak_base = std::move(weak_derived);
|
||||||
@@ -414,7 +414,7 @@ TEST_CASE("Polymorphic edge cases") {
|
|||||||
CHECK(!empty_derived);
|
CHECK(!empty_derived);
|
||||||
|
|
||||||
// Convert empty derived to base
|
// Convert empty derived to base
|
||||||
Ref<Base> empty_base = empty_derived;
|
Ref<Base> empty_base = empty_derived.copy();
|
||||||
CHECK(!empty_base);
|
CHECK(!empty_base);
|
||||||
|
|
||||||
// Move empty derived to base
|
// Move empty derived to base
|
||||||
@@ -427,7 +427,7 @@ TEST_CASE("Polymorphic edge cases") {
|
|||||||
CHECK(!empty_weak_derived.lock());
|
CHECK(!empty_weak_derived.lock());
|
||||||
|
|
||||||
// Convert empty weak derived to weak base
|
// Convert empty weak derived to weak base
|
||||||
WeakRef<Base> empty_weak_base = empty_weak_derived;
|
WeakRef<Base> empty_weak_base = empty_weak_derived.copy();
|
||||||
CHECK(!empty_weak_base.lock());
|
CHECK(!empty_weak_base.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +435,7 @@ TEST_CASE("Polymorphic edge cases") {
|
|||||||
auto derived_ref = make_ref<Derived>(5, 5);
|
auto derived_ref = make_ref<Derived>(5, 5);
|
||||||
|
|
||||||
// Ref<Derived> → WeakRef<Base>
|
// Ref<Derived> → WeakRef<Base>
|
||||||
WeakRef<Base> weak_base_from_ref = derived_ref;
|
WeakRef<Base> weak_base_from_ref = derived_ref.as_weak();
|
||||||
|
|
||||||
// WeakRef<Base> → Ref<Base> via lock
|
// WeakRef<Base> → Ref<Base> via lock
|
||||||
auto base_ref_from_weak = weak_base_from_ref.lock();
|
auto base_ref_from_weak = weak_base_from_ref.lock();
|
||||||
@@ -457,5 +457,5 @@ TEST_CASE("Self-referencing WeakRef pattern") {
|
|||||||
WeakRef<SelfReferencing> self_;
|
WeakRef<SelfReferencing> self_;
|
||||||
};
|
};
|
||||||
auto x = make_ref<SelfReferencing>();
|
auto x = make_ref<SelfReferencing>();
|
||||||
x->self_ = x;
|
x->self_ = x.as_weak();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user