Fix data race in freeing control block
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#include <barrier>
|
||||
#include <doctest/doctest.h>
|
||||
#include <latch>
|
||||
#include <thread>
|
||||
@@ -156,6 +157,63 @@ TEST_CASE("Ref thread safety") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Control block cleanup race condition test") {
|
||||
// This test specifically targets the race condition where both
|
||||
// the last strong reference and last weak reference are destroyed
|
||||
// simultaneously, potentially causing double-free of control block
|
||||
|
||||
const int test_iterations = 10000;
|
||||
|
||||
// Shared state for passing references between threads
|
||||
Ref<TestObject> ptr1;
|
||||
WeakRef<TestObject> ptr2;
|
||||
auto setup = [&]() {
|
||||
ptr1 = make_ref<TestObject>(0);
|
||||
ptr2 = ptr1;
|
||||
};
|
||||
|
||||
// Barrier for synchronization - 2 participants (main thread + worker thread)
|
||||
std::barrier sync_barrier{2};
|
||||
|
||||
std::thread worker_thread([&]() {
|
||||
for (int iter = 0; iter < test_iterations; ++iter) {
|
||||
// Wait for main thread to create the references
|
||||
sync_barrier.arrive_and_wait();
|
||||
|
||||
// Worker thread destroys the weak reference simultaneously with main
|
||||
// thread
|
||||
ptr2.reset();
|
||||
|
||||
// Wait for next iteration
|
||||
sync_barrier.arrive_and_wait();
|
||||
}
|
||||
});
|
||||
|
||||
for (int iter = 0; iter < test_iterations; ++iter) {
|
||||
// Create references
|
||||
setup();
|
||||
|
||||
// Both threads are ready - synchronize for simultaneous destruction
|
||||
sync_barrier.arrive_and_wait();
|
||||
|
||||
// Main thread destroys the strong reference at the same time
|
||||
// as worker thread destroys the weak reference
|
||||
ptr1.reset();
|
||||
|
||||
// Wait for both destructions to complete
|
||||
sync_barrier.arrive_and_wait();
|
||||
|
||||
// Clean up for next iteration
|
||||
ptr1.reset();
|
||||
ptr2.reset();
|
||||
}
|
||||
|
||||
worker_thread.join();
|
||||
|
||||
// If we reach here without segfault/double-free, the test passes
|
||||
// The bug would manifest as a crash or memory corruption
|
||||
}
|
||||
|
||||
TEST_CASE("WeakRef prevents circular references") {
|
||||
SUBCASE("simple weak reference lifecycle") {
|
||||
WeakRef<TestObject> weak_ref;
|
||||
|
||||
Reference in New Issue
Block a user