diff --git a/tests/thread/piprotectedvariable_test.cpp b/tests/thread/piprotectedvariable_test.cpp
index d0b15008..09f52d05 100644
--- a/tests/thread/piprotectedvariable_test.cpp
+++ b/tests/thread/piprotectedvariable_test.cpp
@@ -18,909 +18,119 @@
along with this program. If not, see .
*/
-#include "pimutex.h"
#include "piprotectedvariable.h"
+#include "pistring.h"
+#include "pithread.h"
#include "gtest/gtest.h"
#include
-#include
-#include
-#include
-#include
using namespace std;
-namespace {
-
-constexpr int THREAD_COUNT = 10;
-constexpr int ITERATIONS_PER_THREAD = 1000;
-
-} // anonymous namespace
-
-//! \~\brief Basic functionality tests
-//! \~\english Tests for basic constructor, set, get, and operator= functionality
-//! \~\russian Тесты базовой функциональности конструктора, set, get и оператора=
-TEST(PIProtectedVariable_Basic, ConstructorCreatesValidObject) {
- PIProtectedVariable pv;
- // Note: PIProtectedVariable does not value-initialize its internal var,
- // so we just verify that the object is constructible and we can set/get values
- pv.set(42);
- EXPECT_EQ(pv.get(), 42);
-}
-
-TEST(PIProtectedVariable_Basic, SetCorrectlySetsValue) {
- PIProtectedVariable pv;
- pv.set(42);
- EXPECT_EQ(pv.get(), 42);
-}
-
-TEST(PIProtectedVariable_Basic, SetWithDifferentTypes) {
+// Basic functionality tests
+TEST(PIProtectedVariable_Basic, BasicFunctionality) {
+ // Test basic set/get with different types
PIProtectedVariable pvInt;
PIProtectedVariable pvDouble;
- PIProtectedVariable pvString;
+ PIProtectedVariable pvString;
pvInt.set(123);
pvDouble.set(3.14159);
- pvString.set("Hello, World!");
+ pvString.set(PIString("Hello, World!"));
EXPECT_EQ(pvInt.get(), 123);
EXPECT_DOUBLE_EQ(pvDouble.get(), 3.14159);
- EXPECT_EQ(pvString.get(), "Hello, World!");
-}
+ EXPECT_EQ(pvString.get(), PIString("Hello, World!"));
-TEST(PIProtectedVariable_Basic, GetReturnsCopy) {
- PIProtectedVariable pv;
- pv.set(100);
+ // Test operator=
+ pvInt = 999;
+ EXPECT_EQ(pvInt.get(), 999);
- int val1 = pv.get();
- int val2 = pv.get();
-
- // Modifying copies should not affect the protected variable
- val1 = 200;
- val2 = 300;
-
- EXPECT_EQ(pv.get(), 100);
- EXPECT_EQ(val1, 200);
- EXPECT_EQ(val2, 300);
-}
-
-TEST(PIProtectedVariable_Basic, GetReturnsCopyForObjectTypes) {
- struct TestStruct {
- int x;
- int y;
- TestStruct(): x(0), y(0) {}
- TestStruct(int a, int b): x(a), y(b) {}
- };
-
- PIProtectedVariable pv;
- pv.set(TestStruct(10, 20));
-
- TestStruct val = pv.get();
- val.x = 100;
- val.y = 200;
-
- // Original should be unchanged
- TestStruct original = pv.get();
- EXPECT_EQ(original.x, 10);
- EXPECT_EQ(original.y, 20);
-
- // Verify the copy was modified
- EXPECT_EQ(val.x, 100);
- EXPECT_EQ(val.y, 200);
-}
-
-TEST(PIProtectedVariable_Basic, OperatorAssignmentWorks) {
- PIProtectedVariable pv;
- pv = 999;
- EXPECT_EQ(pv.get(), 999);
-}
-
-TEST(PIProtectedVariable_Basic, OperatorAssignmentReturnsReference) {
- PIProtectedVariable pv;
- PIProtectedVariable & ref = (pv = 42);
- EXPECT_EQ(&ref, &pv);
- EXPECT_EQ(pv.get(), 42);
-}
-
-TEST(PIProtectedVariable_Basic, EmptyDefaultConstructedVariable) {
- // Note: PIProtectedVariable does not value-initialize its internal var
- // We can only test that it's constructible and works after setting values
- PIProtectedVariable pv;
- pv.set(0); // Set to zero explicitly
- EXPECT_EQ(pv.get(), 0);
-
- PIProtectedVariable pvStr;
- pvStr.set(""); // Set to empty explicitly
- EXPECT_TRUE(pvStr.get().empty());
-}
-
-TEST(PIProtectedVariable_Basic, SetWithMoveSemantics) {
- PIProtectedVariable pv;
- string movable = "This is a movable string";
- pv.set(std::move(movable));
- EXPECT_EQ(pv.get(), "This is a movable string");
-}
-
-//! \~\brief Thread safety tests
-//! \~\english Tests for concurrent access safety
-//! \~\russian Тесты потокобезопасности при конкурентном доступе
-TEST(PIProtectedVariable_ThreadSafety, ConcurrentSetOperations) {
- PIProtectedVariable pv;
- atomic start(false);
-
- vector threads;
- for (int i = 0; i < THREAD_COUNT; ++i) {
- threads.emplace_back([&pv, &start, i]() {
- while (!start.load()) {
- this_thread::yield();
- }
- for (int j = 0; j < ITERATIONS_PER_THREAD; ++j) {
- pv.set(i * ITERATIONS_PER_THREAD + j);
- }
- });
- }
-
- start.store(true);
- for (auto & t: threads) {
- t.join();
- }
-
- // The final value should be one of the values set by the threads
- // We can't predict which one, but it should be valid
- int finalVal = pv.get();
- EXPECT_GE(finalVal, 0);
- EXPECT_LT(finalVal, THREAD_COUNT * ITERATIONS_PER_THREAD);
-}
-
-TEST(PIProtectedVariable_ThreadSafety, ConcurrentGetOperations) {
- PIProtectedVariable pv;
- pv.set(42);
- atomic readCount(0);
- atomic wrongCount(0);
- atomic start(false);
-
- vector threads;
- for (int i = 0; i < THREAD_COUNT; ++i) {
- threads.emplace_back([&pv, &readCount, &wrongCount, &start]() {
- while (!start.load()) {
- this_thread::yield();
- }
- for (int j = 0; j < ITERATIONS_PER_THREAD; ++j) {
- int val = pv.get();
- readCount++;
- if (val != 42) {
- wrongCount++;
- }
- }
- });
- }
-
- start.store(true);
- for (auto & t: threads) {
- t.join();
- }
-
- EXPECT_EQ(readCount, THREAD_COUNT * ITERATIONS_PER_THREAD);
- EXPECT_EQ(wrongCount, 0);
-}
-
-TEST(PIProtectedVariable_ThreadSafety, MixedReadWriteOperations) {
- PIProtectedVariable pv;
- pv.set(0); // Initialize with a valid value
- atomic counter(0);
- atomic readCount(0);
- atomic invalidReads(0);
- atomic start(false);
- const int NUM_WRITER_ITERATIONS = 1000;
-
- // Writer thread
- thread writer([&pv, &counter, &start]() {
- while (!start.load()) {
- this_thread::yield();
- }
- for (int i = 0; i < NUM_WRITER_ITERATIONS; ++i) {
- int val = counter.fetch_add(1) + 1;
- pv.set(val);
- }
- });
-
- // Reader threads
- vector readers;
- for (int i = 0; i < THREAD_COUNT; ++i) {
- readers.emplace_back([&pv, &readCount, &invalidReads, &start]() {
- while (!start.load()) {
- this_thread::yield();
- }
- for (int j = 0; j < NUM_WRITER_ITERATIONS; ++j) {
- int val = pv.get();
- readCount++;
- // Verify value is within expected range (0 to NUM_WRITER_ITERATIONS)
- if (val < 0 || val > NUM_WRITER_ITERATIONS) {
- invalidReads++;
- }
- }
- });
- }
-
- start.store(true);
- writer.join();
- for (auto & t: readers) {
- t.join();
- }
-
- // Verify that counter reached expected value
- EXPECT_EQ(counter, NUM_WRITER_ITERATIONS);
-
- // Verify that all reads were valid
- EXPECT_EQ(readCount, THREAD_COUNT * NUM_WRITER_ITERATIONS);
- EXPECT_EQ(invalidReads, 0);
-}
-
-TEST(PIProtectedVariable_ThreadSafety, HeavyConcurrentAccess) {
- PIProtectedVariable pv;
- pv.set(0); // Initialize with a valid value
- atomic writeCount(0);
- atomic readCount(0);
- atomic done(false);
- atomic start(false);
-
- // Multiple writers
- vector writers;
- for (int i = 0; i < THREAD_COUNT / 2; ++i) {
- writers.emplace_back([&pv, &writeCount, &done, &start, i]() {
- while (!start.load()) {
- this_thread::yield();
- }
- while (!done.load()) {
- pv.set(i * 1000 + writeCount.fetch_add(1));
- }
- });
- }
-
- // Multiple readers
- vector readers;
- for (int i = 0; i < THREAD_COUNT / 2; ++i) {
- readers.emplace_back([&pv, &readCount, &done, &start]() {
- while (!start.load()) {
- this_thread::yield();
- }
- while (!done.load()) {
- int val = pv.get();
- readCount++;
- // Verify value is non-negative (writers only write positive values)
- if (val < 0) {
- FAIL() << "Invalid value read: " << val;
- }
- }
- });
- }
-
- start.store(true);
-
- // Run for a short time
- this_thread::sleep_for(chrono::milliseconds(500));
- done.store(true);
-
- for (auto & t: writers) {
- t.join();
- }
- for (auto & t: readers) {
- t.join();
- }
-
- EXPECT_GT(writeCount, 0);
- EXPECT_GT(readCount, 0);
-}
-
-//! \~\brief Pointer wrapper tests
-//! \~\english Tests for getRef() and Pointer wrapper functionality
-//! \~\russian Тесты обертки getRef() и Pointer
-TEST(PIProtectedVariable_Pointer, GetRefReturnsValidPointer) {
- PIProtectedVariable pv;
- pv.set(42);
-
- auto ptr = pv.getRef();
- EXPECT_NE(&(*ptr), nullptr);
- EXPECT_EQ(*ptr, 42);
-}
-
-TEST(PIProtectedVariable_Pointer, OperatorArrowWorks) {
+ // Test getRef() with Pointer
struct TestStruct {
int x = 10;
int y = 20;
int getValue() const { return x + y; }
};
- PIProtectedVariable pv;
- pv.set(TestStruct());
+ PIProtectedVariable pvStruct;
- auto ptr = pv.getRef();
+ auto ptr = pvStruct.getRef();
EXPECT_EQ(ptr->x, 10);
EXPECT_EQ(ptr->y, 20);
EXPECT_EQ(ptr->getValue(), 30);
-}
-TEST(PIProtectedVariable_Pointer, OperatorDerefWorks) {
- PIProtectedVariable pv;
- pv.set("original");
+ // Modify through pointer
+ *ptr = TestStruct();
+ ptr->x = 100;
+ EXPECT_EQ(pvStruct.get().x, 100);
- auto ptr = pv.getRef();
- *ptr = "modified";
-
- EXPECT_EQ(pv.get(), "modified");
-}
-
-TEST(PIProtectedVariable_Pointer, OperatorDerefAllowsModification) {
- PIProtectedVariable pv;
- pv.set(100);
-
- auto ptr = pv.getRef();
- (*ptr) += 50;
-
- EXPECT_EQ(pv.get(), 150);
-}
-
-TEST(PIProtectedVariable_Pointer, MutexLockedWhenPointerCreated) {
- PIProtectedVariable pv;
- pv.set(42);
-
- atomic gotRef(false);
- atomic start(false);
-
- // Start blocker thread but don't let it run yet
- thread blocker([&pv, &gotRef, &start]() {
- while (!start.load()) {
- this_thread::yield();
- }
- auto p = pv.getRef();
- gotRef.store(true);
- });
-
- // Get the lock in main thread
- auto ptr = pv.getRef();
-
- // Now let blocker try to acquire
- start.store(true);
-
- // Give the thread time to try to acquire the lock
- this_thread::sleep_for(chrono::milliseconds(100));
-
- // The blocker should NOT have acquired the lock yet
- EXPECT_FALSE(gotRef.load());
-
- // Release the first pointer by letting it go out of scope
- ptr.~Pointer();
-
- blocker.join();
- EXPECT_TRUE(gotRef.load());
-}
-
-TEST(PIProtectedVariable_Pointer, MutexUnlockedWhenPointerDestroyed) {
- PIProtectedVariable pv;
- pv.set(42);
-
- {
- auto ptr = pv.getRef();
- // ptr holds the lock
- (*ptr) = 100;
- } // ptr destroyed here, lock released
-
- // Now we should be able to get another reference immediately
- auto ptr2 = pv.getRef();
- EXPECT_EQ(*ptr2, 100);
-}
-
-TEST(PIProtectedVariable_Pointer, PointerModifiesProtectedValue) {
- PIProtectedVariable pv;
- pv.set("Hello");
-
- {
- auto ptr = pv.getRef();
- *ptr += " World";
- }
-
- EXPECT_EQ(pv.get(), "Hello World");
-}
-
-TEST(PIProtectedVariable_Pointer, PointerWithComplexTypes) {
- struct ComplexType {
- vector data;
- map mapping;
-
- ComplexType() {
- data = {1, 2, 3, 4, 5};
- mapping["one"] = 1;
- mapping["two"] = 2;
- }
- };
-
- PIProtectedVariable pv;
- pv.set(ComplexType());
-
- auto ptr = pv.getRef();
- ptr->data.push_back(6);
- ptr->mapping["three"] = 3;
-
- EXPECT_EQ(pv.get().data.size(), 6u);
- EXPECT_EQ(pv.get().mapping.size(), 3u);
- EXPECT_EQ(pv.get().mapping["three"], 3);
-}
-
-//! \~\brief Move semantics tests
-//! \~\english Tests for move constructor and lock ownership transfer
-//! \~\russian Тесты конструктора перемещения и передачи владения блокировкой
-TEST(PIProtectedVariable_Move, MoveConstructorWorks) {
- PIProtectedVariable pv;
- pv.set(42);
-
- auto ptr1 = pv.getRef();
+ // Test move semantics for Pointer
+ pvInt.set(42);
+ auto ptr1 = pvInt.getRef();
auto ptr2 = std::move(ptr1);
-
- // ptr2 should have access
EXPECT_EQ(*ptr2, 42);
*ptr2 = 100;
- EXPECT_EQ(pv.get(), 100);
+ EXPECT_EQ(pvInt.get(), 100);
}
-TEST(PIProtectedVariable_Move, MoveConstructorTransfersLockOwnership) {
- PIProtectedVariable pv;
- pv.set(42);
-
- // We can't directly test unlock count without modifying the class,
- // but we can test the behavior: after move, only the target should unlock
-
- auto ptr1 = pv.getRef();
-
- // ptr1 holds the lock
- bool canAcquire = false;
- thread tryAcquire([&pv, &canAcquire]() {
- auto p = pv.getRef();
- canAcquire = true;
- });
-
- this_thread::sleep_for(chrono::milliseconds(50));
- EXPECT_FALSE(canAcquire); // Lock still held by ptr1
-
- // Move ptr1 to ptr2
- auto ptr2 = std::move(ptr1);
-
- // ptr2 should still hold the lock
- this_thread::sleep_for(chrono::milliseconds(50));
- EXPECT_FALSE(canAcquire); // Lock still held (now by ptr2)
-
- // Destroy ptr2
- ptr2.~Pointer();
-
- tryAcquire.join();
- EXPECT_TRUE(canAcquire); // Now lock is free
-}
-
-TEST(PIProtectedVariable_Move, SourceDoesNotUnlockAfterMove) {
- PIProtectedVariable pv;
- pv.set(42);
-
- // If source unlocked after move, we would get a double-unlock error
- // or undefined behavior. This test verifies that moving works correctly.
-
- auto ptr1 = pv.getRef();
- (*ptr1) = 100;
-
- // Move ptr1
- auto ptr2 = std::move(ptr1);
-
- // ptr2 should work correctly
- (*ptr2) = 200;
- EXPECT_EQ(pv.get(), 200);
-
- // When ptr1 is destroyed, it should NOT unlock (ownsLock should be false)
- // When ptr2 is destroyed, it SHOULD unlock
- // This should complete without any errors
-}
-
-TEST(PIProtectedVariable_Move, TargetUnlocksInDestructor) {
- PIProtectedVariable pv;
- pv.set(42);
-
- bool lockReleased = false;
-
- {
- auto ptr1 = pv.getRef();
- auto ptr2 = std::move(ptr1);
- // ptr2 holds the lock, ptr1 does not
- } // ptr2 destroyed here, should release lock
-
- // If we get here without deadlock, the lock was properly released
- auto ptr3 = pv.getRef();
- lockReleased = true;
-
- EXPECT_TRUE(lockReleased);
-}
-
-TEST(PIProtectedVariable_Move, UnlockCalledExactlyOnceAfterMove) {
- PIProtectedVariable pv;
- pv.set(42);
-
- // Test that after move:
- // 1. Source Pointer's destructor does NOT call unlock()
- // 2. Target Pointer's destructor DOES call unlock()
- // 3. Total unlock() calls = 1 (not 2!)
-
- {
- auto ptr1 = pv.getRef();
- auto ptr2 = std::move(ptr1);
- // ptr1.ownsLock should be false
- // ptr2.ownsLock should be true
-
- // Both go out of scope
- // Only ptr2 should unlock
- }
-
- // If we get here without issues, exactly one unlock happened
- EXPECT_EQ(pv.get(), 42);
-}
-
-TEST(PIProtectedVariable_Move, MoveWithRVODisabled) {
- // This test should be compiled with -fno-elide-constructors
- // to ensure move constructor is actually called
-
- PIProtectedVariable pv(42);
-
- auto ptr_src = pv.getRef();
- *ptr_src = 99;
- auto ptr = std::move(ptr_src);
- *ptr = 100;
-
- EXPECT_EQ(pv.get(), 100);
-}
-
-TEST(PIProtectedVariable_Move, MoveConstructorWithComplexTypes) {
- struct ComplexType {
- vector data;
- string name;
-
- ComplexType(): data{1, 2, 3}, name("test") {}
- };
-
- PIProtectedVariable pv;
- pv.set(ComplexType());
-
- auto ptr1 = pv.getRef();
- ptr1->data.push_back(4);
- ptr1->name = "modified";
-
- auto ptr2 = std::move(ptr1);
- ptr2->data.push_back(5);
-
- EXPECT_EQ(pv.get().data.size(), 5u);
- EXPECT_EQ(pv.get().name, "modified");
-}
-
-//! \~\brief Copy prevention tests
-//! \~\english Tests that verify copy operations are deleted
-//! \~\russian Тесты проверки что операции копирования удалены
-TEST(PIProtectedVariable_CopyPrevention, CopyConstructorDeleted) {
- PIProtectedVariable pv;
- pv.set(42);
-
- auto ptr = pv.getRef();
-
- // This should not compile:
- // auto ptr2 = ptr; // Copy constructor is deleted
-
- // We verify this at compile time by ensuring the code compiles
- // without attempting to copy
- EXPECT_EQ(*ptr, 42);
-}
-
-TEST(PIProtectedVariable_CopyPrevention, CopyAssignmentDeleted) {
- PIProtectedVariable pv;
- pv.set(42);
-
- auto ptr1 = pv.getRef();
- auto ptr2 = pv.getRef(); // This will block until ptr1 is released
-
- // This should not compile:
- // ptr1 = ptr2; // Copy assignment is deleted
-
- (*ptr2) = 100;
- EXPECT_EQ(pv.get(), 100);
-}
-
-TEST(PIProtectedVariable_CopyPrevention, MoveAssignmentDeleted) {
- PIProtectedVariable pv;
- pv.set(42);
-
- auto ptr1 = pv.getRef();
- auto ptr2 = pv.getRef(); // This will block until ptr1 is released
-
- // This should not compile:
- // ptr1 = std::move(ptr2); // Move assignment is deleted
-
- (*ptr2) = 100;
- EXPECT_EQ(pv.get(), 100);
-}
-
-TEST(PIProtectedVariable_CopyPrevention, CompileTimeCheck) {
- // Compile-time verification that copy operations are deleted
- // These static_assert statements will fail to compile if copy operations exist
-
+// Thread safety tests
+TEST(PIProtectedVariable_ThreadSafety, ConcurrentReadWrite) {
PIProtectedVariable pv;
- // The fact that this file compiles proves that:
- // 1. Pointer copy constructor is deleted
- // 2. Pointer copy assignment is deleted
- // 3. Pointer move assignment is deleted
+ atomic writeCount(0);
+ atomic readCount(0);
+ atomic invalidReads(0);
+ const int NUM_ITERATIONS = 1000;
+ const int NUM_WRITERS = 10;
+ const int NUM_READERS = 20;
+ const int TOTAL_WRITES = NUM_WRITERS * NUM_ITERATIONS;
- EXPECT_TRUE(true); // Placeholder assertion
-}
+ // Collect thread handles for joining
+ PIVector threads;
-//! \~\brief Edge case tests
-//! \~\english Tests for edge cases and exception safety
-//! \~\russian Тесты граничных случаев и исключительной безопасности
-TEST(PIProtectedVariable_EdgeCases, MultipleGetRefCallsSequential) {
- PIProtectedVariable pv;
- pv.set(0);
-
- for (int i = 0; i < 100; ++i) {
- auto ptr = pv.getRef();
- (*ptr)++;
- }
-
- EXPECT_EQ(pv.get(), 100);
-}
-
-TEST(PIProtectedVariable_EdgeCases, NestedGetRefInSeparateScopes) {
- PIProtectedVariable pv;
- pv.set(0);
-
- {
- auto ptr1 = pv.getRef();
- (*ptr1) += 10;
- }
-
- {
- auto ptr2 = pv.getRef();
- (*ptr2) += 20;
- }
-
- {
- auto ptr3 = pv.getRef();
- (*ptr3) += 30;
- }
-
- EXPECT_EQ(pv.get(), 60);
-}
-
-TEST(PIProtectedVariable_EdgeCases, LargeNumberOfThreads) {
- PIProtectedVariable pv;
- atomic counter(0);
- atomic start(false);
- const int NUM_THREADS = 50;
-
- vector threads;
- for (int i = 0; i < NUM_THREADS; ++i) {
- threads.emplace_back([&pv, &counter, &start]() {
- while (!start.load()) {
- this_thread::yield();
+ // Create writer threads
+ for (int i = 0; i < NUM_WRITERS; ++i) {
+ threads.push_back(new PIThread([&pv, &writeCount]() {
+ for (int j = 0; j < NUM_ITERATIONS; ++j) {
+ auto val = pv.getRef();
+ (*val)++;
+ writeCount++;
}
- for (int j = 0; j < 100; ++j) {
- int val = counter.fetch_add(1);
- pv.set(val);
- int readVal = pv.get();
- EXPECT_GE(readVal, 0);
- }
- });
+ }));
}
- start.store(true);
- for (auto & t: threads) {
- t.join();
- }
-
- EXPECT_EQ(counter, NUM_THREADS * 100);
-}
-
-TEST(PIProtectedVariable_EdgeCases, ValueInitializationOfDifferentTypes) {
- // Note: PIProtectedVariable does not value-initialize its internal var
- // We test that setting and getting works for different types
- PIProtectedVariable pvInt;
- PIProtectedVariable pvDouble;
- PIProtectedVariable pvBool;
- PIProtectedVariable pvString;
- PIProtectedVariable pvVoidPtr;
-
- pvInt.set(0);
- pvDouble.set(0.0);
- pvBool.set(false);
- pvString.set("");
- pvVoidPtr.set(nullptr);
-
- EXPECT_EQ(pvInt.get(), 0);
- EXPECT_DOUBLE_EQ(pvDouble.get(), 0.0);
- EXPECT_FALSE(pvBool.get());
- EXPECT_TRUE(pvString.get().empty());
- EXPECT_EQ(pvVoidPtr.get(), nullptr);
-}
-
-TEST(PIProtectedVariable_EdgeCases, NegativeAndExtremeValues) {
- PIProtectedVariable pv;
-
- pv.set(INT_MIN);
- EXPECT_EQ(pv.get(), INT_MIN);
-
- pv.set(INT_MAX);
- EXPECT_EQ(pv.get(), INT_MAX);
-
- pv.set(-1);
- EXPECT_EQ(pv.get(), -1);
-
- pv.set(0);
- EXPECT_EQ(pv.get(), 0);
-}
-
-TEST(PIProtectedVariable_EdgeCases, EmptyStringAndContainer) {
- PIProtectedVariable pvStr;
- PIProtectedVariable> pvVec;
-
- pvStr.set("");
- EXPECT_TRUE(pvStr.get().empty());
-
- pvVec.set(vector());
- EXPECT_TRUE(pvVec.get().empty());
-}
-
-TEST(PIProtectedVariable_EdgeCases, PointerLifetime) {
- PIProtectedVariable pv;
- pv.set(42);
-
- int * rawPtr = nullptr;
- {
- auto ptr = pv.getRef();
- rawPtr = &(*ptr);
- // ptr destroyed here
- }
-
- // rawPtr still points to valid memory (pv.var)
- // but we shouldn't modify through it without holding the lock
- EXPECT_EQ(*rawPtr, 42);
-}
-
-//! \~\brief Exception safety tests
-//! \~\english Tests for exception safety
-//! \~\russian Тесты исключительной безопасности
-TEST(PIProtectedVariable_ExceptionSafety, SetWithMoveDoesNotLeaveInBadState) {
- struct MovesOnly {
- int value;
- MovesOnly(): value(0) {}
- MovesOnly(int v): value(v) {}
- MovesOnly(const MovesOnly &) = delete; // Copy is deleted
- MovesOnly(MovesOnly &&) noexcept = default;
- MovesOnly & operator=(const MovesOnly &) = delete;
- MovesOnly & operator=(MovesOnly &&) noexcept = default;
- };
-
- PIProtectedVariable pv;
- pv.set(MovesOnly(42));
-
- // Use getRef to access the value since get() requires copy
- auto ptr = pv.getRef();
- EXPECT_EQ(ptr->value, 42);
-}
-
-TEST(PIProtectedVariable_ExceptionSafety, GetRefDoesNotLeakLockOnException) {
- PIProtectedVariable pv;
- pv.set(0);
-
- bool exceptionThrown = false;
- try {
- auto ptr = pv.getRef();
- (*ptr) = 100;
- // ptr goes out of scope normally, lock released
- } catch (...) {
- exceptionThrown = true;
- }
-
- EXPECT_FALSE(exceptionThrown);
- EXPECT_EQ(pv.get(), 100);
-
- // If lock was leaked, this would deadlock
- auto ptr2 = pv.getRef();
- EXPECT_EQ(*ptr2, 100);
-}
-
-//! \~\brief Integration tests
-//! \~\english Integration tests combining multiple features
-//! \~\russian Интеграционные тесты, сочетающие несколько функций
-TEST(PIProtectedVariable_Integration, ProducerConsumerPattern) {
- PIProtectedVariable buffer;
- atomic done(false);
- atomic produced(0);
- atomic consumed(0);
- atomic start(false);
- const int NUM_ITEMS = 1000;
-
- // Producer
- thread producer([&]() {
- while (!start.load()) {
- this_thread::yield();
- }
- for (int i = 0; i < NUM_ITEMS; ++i) {
- auto ptr = buffer.getRef();
- *ptr = i;
- produced++;
- }
- done.store(true);
- });
-
- // Consumer - consumes exactly NUM_ITEMS
- thread consumer([&]() {
- while (!start.load()) {
- this_thread::yield();
- }
- for (int i = 0; i < NUM_ITEMS; ++i) {
- // Wait for producer to produce something
- while (produced.load() <= consumed.load() && !done.load()) {
- this_thread::yield();
- }
- auto ptr = buffer.getRef();
- int val = *ptr;
- consumed++;
- (void)val; // Suppress unused warning
- }
- });
-
- start.store(true);
- producer.join();
- consumer.join();
-
- EXPECT_EQ(produced, NUM_ITEMS);
- EXPECT_EQ(consumed, NUM_ITEMS);
-}
-
-TEST(PIProtectedVariable_Integration, ReadMostlyPattern) {
- PIProtectedVariable config;
- config.set(100);
-
- atomic totalReads(0);
- atomic wrongReads(0);
- atomic start(false);
- atomic done(false);
-
- // Readers
- vector readers;
- for (int i = 0; i < THREAD_COUNT; ++i) {
- readers.emplace_back([&]() {
- while (!start.load()) {
- this_thread::yield();
- }
- while (!done.load()) {
- int val = config.get();
- totalReads++;
- if (val < 100 || val > 200) {
- wrongReads++;
+ // Create reader threads
+ for (int i = 0; i < NUM_READERS; ++i) {
+ threads.push_back(new PIThread([&pv, &invalidReads, &readCount]() {
+ for (int j = 0; j < NUM_ITERATIONS; ++j) {
+ auto val = pv.get();
+ readCount++;
+ // Value should always be in valid range [0, TOTAL_WRITES]
+ if (val < 0 || val > TOTAL_WRITES) {
+ invalidReads++;
}
}
- });
+ }));
}
- // Writer
- thread writer([&]() {
- while (!start.load()) {
- this_thread::yield();
- }
- for (int i = 0; i < 100; ++i) {
- config.set(100 + i);
- this_thread::sleep_for(chrono::milliseconds(10));
- }
- done.store(true);
- });
-
- start.store(true);
- writer.join();
- for (auto & t: readers) {
- t.join();
+ // Start all threads
+ for (auto * t: threads) {
+ t->startOnce();
}
- EXPECT_GT(totalReads, 0);
- EXPECT_EQ(wrongReads, 0);
+ // Wait for all threads to finish
+ for (auto * t: threads) {
+ t->waitForFinish(PISystemTime::fromSeconds(5));
+ delete t;
+ }
+
+ // Verify results
+ EXPECT_EQ(writeCount, TOTAL_WRITES);
+ EXPECT_EQ(readCount, NUM_READERS * NUM_ITERATIONS);
+ EXPECT_EQ(invalidReads, 0) << "All reads should return valid values in range [0, " << TOTAL_WRITES << "]";
+
+ // Final value should be TOTAL_WRITES
+ int finalVal = pv.get();
+ EXPECT_EQ(finalVal, TOTAL_WRITES);
}