diff --git a/libs/main/thread/piprotectedvariable.h b/libs/main/thread/piprotectedvariable.h index 7c3f246c..650d79f5 100644 --- a/libs/main/thread/piprotectedvariable.h +++ b/libs/main/thread/piprotectedvariable.h @@ -25,6 +25,7 @@ #ifndef PIPROTECTEDVARIABLE_H #define PIPROTECTEDVARIABLE_H +#include "picout.h" #include "pimutex.h" //! \~\ingroup Thread @@ -34,6 +35,8 @@ template class PIP_EXPORT PIProtectedVariable { public: + PIProtectedVariable(T v = T()): var(std::move(v)) {} + //! \~\brief //! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked. //! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным. @@ -44,17 +47,23 @@ public: public: //! \~\english Move constructor - transfers ownership of the lock. //! \~russian Конструктор перемещения - передает владение блокировкой. - Pointer(Pointer && other) noexcept: pv(other.pv), ownsLock(other.ownsLock) { other.ownsLock = false; } + Pointer(Pointer && other): pv(other.pv) { other.can_unlock = false; }; - //! \~\english Move assignment is deleted - Pointer can only be moved once. - //! \~russian Оператор перемещения удален - Pointer можно переместить только один раз. - Pointer & operator=(Pointer &&) = delete; + //! \~\english Move assignment - transfers ownership of the lock. + //! \~russian Оператор перемещения - передает владение блокировкой. + Pointer & operator=(Pointer && other) { + pv = other.pv; + other.can_unlock = false; + }; //! \~\english Destroys wrapper and releases the mutex. //! \~russian Уничтожает обертку и освобождает мьютекс. ~Pointer() { - if (ownsLock) pv.mutex.unlock(); + if (can_unlock) { + pv.mutex.unlock(); + piCout << "mutex.unlock()" << &(pv.mutex); + } } //! \~english Returns pointer access to the protected value. @@ -66,11 +75,14 @@ public: T & operator*() { return pv.var; } private: - Pointer() = delete; - Pointer(PIProtectedVariable & v): pv(v), ownsLock(true) {} + explicit Pointer() = delete; + explicit Pointer(PIProtectedVariable & v): pv(v) { + pv.mutex.lock(); + piCout << "mutex.lock()" << &(pv.mutex); + } PIProtectedVariable & pv; - bool ownsLock = true; + bool can_unlock = true; }; //! \~english Replaces the protected value with \a v. @@ -82,10 +94,7 @@ public: //! \~english Locks the value and returns wrapper-based access to it. //! \~russian Блокирует значение и возвращает обертку для доступа к нему. - Pointer getRef() { - mutex.lock(); - return Pointer(*this); - } + Pointer getRef() { return Pointer(*this); } //! \~english Returns a copy of the protected value. //! \~russian Возвращает копию защищенного значения. @@ -104,7 +113,7 @@ public: private: mutable PIMutex mutex; - T var; + T var = {}; }; diff --git a/main.cpp b/main.cpp index cf2f6235..fd98c891 100644 --- a/main.cpp +++ b/main.cpp @@ -20,6 +20,27 @@ inline PIByteArray SMBusTypeInfo_genHash(PIString n) { int main(int argc, char * argv[]) { + PIProtectedVariable pv(3.0); + piCout << pv.get(); + { + auto ref = pv.getRef(); + piCout << *ref; + *ref = 11.; + piCout << *ref; + } + piCout << pv.get(); + { + auto ref = pv.getRef(); + piCout << *ref; + *ref = 12.; + piCout << *ref; + auto ref2 = std::move(ref); + piCout << *ref2; + } + piCout << pv.get(); + return 0; + + PICrypt _crypt; // auto ba = PIFile::readAll("logo.png"); PIString str = "hello!"_a; diff --git a/tests/thread/piprotectedvariable_test.cpp b/tests/thread/piprotectedvariable_test.cpp index e9bdc1ff..d0b15008 100644 --- a/tests/thread/piprotectedvariable_test.cpp +++ b/tests/thread/piprotectedvariable_test.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -35,7 +34,6 @@ namespace { constexpr int THREAD_COUNT = 10; constexpr int ITERATIONS_PER_THREAD = 1000; -constexpr int WAIT_THREAD_TIME_MS = 1000; } // anonymous namespace @@ -229,7 +227,7 @@ TEST(PIProtectedVariable_ThreadSafety, MixedReadWriteOperations) { // Reader threads vector readers; for (int i = 0; i < THREAD_COUNT; ++i) { - readers.emplace_back([&pv, &readCount, &invalidReads, &start, NUM_WRITER_ITERATIONS]() { + readers.emplace_back([&pv, &readCount, &invalidReads, &start]() { while (!start.load()) { this_thread::yield(); } @@ -468,8 +466,6 @@ TEST(PIProtectedVariable_Move, MoveConstructorTransfersLockOwnership) { PIProtectedVariable pv; pv.set(42); - atomic unlockCount(0); - // We can't directly test unlock count without modifying the class, // but we can test the behavior: after move, only the target should unlock @@ -503,8 +499,6 @@ TEST(PIProtectedVariable_Move, SourceDoesNotUnlockAfterMove) { PIProtectedVariable pv; pv.set(42); - atomic deadlockOccurred(false); - // If source unlocked after move, we would get a double-unlock error // or undefined behavior. This test verifies that moving works correctly. @@ -569,15 +563,11 @@ TEST(PIProtectedVariable_Move, MoveWithRVODisabled) { // This test should be compiled with -fno-elide-constructors // to ensure move constructor is actually called - PIProtectedVariable pv; - pv.set(42); + PIProtectedVariable pv(42); - auto createAndMove = [&pv]() -> decltype(pv.getRef()) { - auto ptr = pv.getRef(); - return std::move(ptr); - }; - - auto ptr = createAndMove(); + auto ptr_src = pv.getRef(); + *ptr_src = 99; + auto ptr = std::move(ptr_src); *ptr = 100; EXPECT_EQ(pv.get(), 100);