15#if qStroika_Foundation_Debug_DefaultTracingOn
16 void DbgTraceHelper_ (
const wchar_t* w1,
const optional<std::wstring>& w2);
28 template <
typename T,
typename TRAITS>
29 template <
typename... ARGUMENT_TYPES>
31 requires (constructible_from<T, ARGUMENT_TYPES...>)
32 : fProtectedValue_ (forward<ARGUMENT_TYPES> (args)...)
35 template <
typename T,
typename TRAITS>
37 : fProtectedValue_{src.cget ().load ()}
40 template <
typename T,
typename TRAITS>
41 inline auto Synchronized<T, TRAITS>::operator= (
const Synchronized& rhs) -> Synchronized&
43 if (&rhs !=
this) [[likely]] {
44 auto value = rhs.cget ().
load ();
45 [[maybe_unused]] lock_guard critSec{fMutex_};
46 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
47 NoteLockStateChanged_ (L
"Locked");
49 fProtectedValue_ = value;
53 template <
typename T,
typename TRAITS>
54 inline auto Synchronized<T, TRAITS>::operator= (T&& rhs) -> Synchronized&
56 [[maybe_unused]] lock_guard critSec{fMutex_};
57 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
58 NoteLockStateChanged_ (L
"Locked");
59 fProtectedValue_ = move (rhs);
63 template <
typename T,
typename TRAITS>
64 inline auto Synchronized<T, TRAITS>::operator= (
const T& rhs) -> Synchronized&
66 [[maybe_unused]] lock_guard critSec{fMutex_};
67 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
68 NoteLockStateChanged_ (L
"Locked");
69 fProtectedValue_ = rhs;
73 template <
typename T,
typename TRAITS>
75 requires (TRAITS::kIsRecursiveReadMutex)
79 template <
typename T,
typename TRAITS>
81 requires (TRAITS::kIsRecursiveReadMutex)
83 ReadLockType_ fromCritSec{fMutex_};
84 return fProtectedValue_;
86 template <
typename T,
typename TRAITS>
88 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportsTimedLocks)
90 ReadLockType_ critSec{fMutex_, tryFor};
91 if (not critSec) [[unlikely]] {
94 return fProtectedValue_;
96 template <
typename T,
typename TRAITS>
98 requires (TRAITS::kIsRecursiveLockMutex)
100 [[maybe_unused]] lock_guard critSec{fMutex_};
101 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
102 NoteLockStateChanged_ (L
"Locked");
104 fProtectedValue_ = v;
106 template <
typename T,
typename TRAITS>
108 requires (TRAITS::kIsRecursiveLockMutex)
110 [[maybe_unused]] lock_guard critSec{fMutex_};
111 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
112 NoteLockStateChanged_ (L
"Locked");
114 fProtectedValue_ = std::move (v);
116 template <
typename T,
typename TRAITS>
118 requires (TRAITS::kIsRecursiveLockMutex and TRAITS::kSupportsTimedLocks)
120 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
121 if (not critSec) [[unlikely]] {
124 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
125 NoteLockStateChanged_ (L
"Locked");
127 fProtectedValue_ = v;
129 template <
typename T,
typename TRAITS>
131 requires (TRAITS::kIsRecursiveLockMutex and TRAITS::kSupportsTimedLocks)
133 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
134 if (not critSec) [[unlikely]] {
137 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
138 NoteLockStateChanged_ (L
"Locked");
140 fProtectedValue_ = std::move (v);
142 template <
typename T,
typename TRAITS>
147 template <
typename T,
typename TRAITS>
149 requires (TRAITS::kSupportsTimedLocks)
151 ReadLockType_ critSec{fMutex_, tryFor};
152 if (not critSec) [[unlikely]] {
155 return ReadableReference{
this, move (critSec)};
157 template <
typename T,
typename TRAITS>
160 return WritableReference{
this};
162 template <
typename T,
typename TRAITS>
164 requires (TRAITS::kSupportsTimedLocks)
166 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
167 if (not critSec) [[unlikely]] {
170 return WritableReference{
this, move (critSec)};
172 template <
typename T,
typename TRAITS>
173 inline auto Synchronized<T, TRAITS>::operator->() const -> ReadableReference
175 return ReadableReference{
this};
177 template <
typename T,
typename TRAITS>
179 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportSharedLocks)
181#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
184 fMutex_.lock_shared ();
185 NoteLockStateChanged_ (L
"Locked Shared");
187 template <
typename T,
typename TRAITS>
189 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportSharedLocks)
191#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
194 fMutex_.unlock_shared ();
195 NoteLockStateChanged_ (L
"Unlocked Shared");
197 template <
typename T,
typename TRAITS>
199 requires (TRAITS::kIsRecursiveReadMutex)
201#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
205 NoteLockStateChanged_ (L
"Locked");
208 template <
typename T,
typename TRAITS>
210 requires (TRAITS::kIsRecursiveReadMutex)
212#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
215 bool result = fMutex_.try_lock ();
216 if (result) [[likely]] {
217 NoteLockStateChanged_ (L
"Locked");
222 template <
typename T,
typename TRAITS>
224 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportsTimedLocks)
226#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
229 bool result = fMutex_.try_lock_for (tryFor);
230 if (result) [[likely]] {
231 NoteLockStateChanged_ (L
"Locked");
236 template <
typename T,
typename TRAITS>
238 requires (TRAITS::kIsRecursiveReadMutex)
240#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
243 NoteLockStateChanged_ (L
"Unlocked");
246 template <
typename T,
typename TRAITS>
248 requires (TRAITS::kIsRecursiveReadMutex and equality_comparable<T>)
250 return load () == rhs.
load ();
252 template <
typename T,
typename TRAITS>
253 inline bool Synchronized<T, TRAITS>::operator== (
const T& rhs)
const
254 requires (TRAITS::kIsRecursiveReadMutex and equality_comparable<T>)
256 return load () == rhs;
258 template <
typename T,
typename TRAITS>
259 inline auto Synchronized<T, TRAITS>::operator<=> (
const Synchronized& rhs)
const
260 requires (TRAITS::kIsRecursiveReadMutex and three_way_comparable<T>)
262 return load () <=> rhs.load ();
264 template <
typename T,
typename TRAITS>
265 inline auto Synchronized<T, TRAITS>::operator<=> (
const T& rhs)
const
266 requires (TRAITS::kIsRecursiveReadMutex and three_way_comparable<T>)
268 return load () <=> rhs;
270 template <
typename T,
typename TRAITS>
272 const function<
void (WritableReference&&)>& doWithWriteLock,
274 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
276 return UpgradeLockNonAtomicallyQuietly (
278 [&] (WritableReference&& wRef, [[maybe_unused]]
bool interveningWriteLock) {
279 if (interveningWriteLock) [[unlikely]] {
280#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
281 DbgTrace (
"in UpgradeLockNonAtomicallyQuietly - turning interveningWriteLock into fake timeout");
286 doWithWriteLock (std::move (wRef));
292 template <
typename T,
typename TRAITS>
294 const function<
bool (WritableReference&&,
bool interveningWriteLock)>& doWithWriteLock,
296 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
299#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
300 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
304 Require (lockBeingUpgraded->fSharedLock_.mutex () == &fMutex_);
305 Require (lockBeingUpgraded->fSharedLock_.owns_lock ());
306 auto writeLockCountBeforeReleasingReadLock = fWriteLockCount_;
307 fMutex_.unlock_shared ();
309 fMutex_.lock_shared ();
310 NoteLockStateChanged_ (L
"in Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly finally relocked shared");
312 typename TRAITS::WriteLockType upgradeLock{fMutex_, std::defer_lock};
313 if (timeout >= Time::DurationSeconds::max ()) {
317 if (not upgradeLock.try_lock_for (timeout)) {
321 NoteLockStateChanged_ (L
"in Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly acquired Lock");
322 WritableReference wr = WritableReference{
this, std::move (upgradeLock)};
323 bool interveningWriteLock = fWriteLockCount_ > 1 + writeLockCountBeforeReleasingReadLock;
325 return doWithWriteLock (std::move (wr), interveningWriteLock);
327 template <
typename T,
typename TRAITS>
329 const function<
void (WritableReference&&)>& doWithWriteLock,
331 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
333#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
334 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
337 if (not UpgradeLockNonAtomicallyQuietly (lockBeingUpgraded, doWithWriteLock, timeout)) [[unlikely]] {
341 template <
typename T,
typename TRAITS>
343 const function<
bool (WritableReference&&,
bool interveningWriteLock)>& doWithWriteLock,
345 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
347#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
348 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
351 if (not UpgradeLockNonAtomicallyQuietly (lockBeingUpgraded, doWithWriteLock, timeout)) [[unlikely]] {
355 template <
typename T,
typename TRAITS>
356 inline void Synchronized<T, TRAITS>::NoteLockStateChanged_ ([[maybe_unused]]
const wchar_t* m)
const noexcept
358 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
359 if (this->fDbgTraceLocksName) {
360#if qStroika_Foundation_Debug_DefaultTracingOn
361 Private_::DbgTraceHelper_ (m, this->fDbgTraceLocksName);
372 template <
typename T,
typename TRAITS>
377#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
378 DbgTrace (L
"ReadableReference::CTOR -- locks (_eExternallyLocked)");
380 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
381 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
384 template <
typename T,
typename TRAITS>
385 inline Synchronized<T, TRAITS>::ReadableReference::ReadableReference (
const Synchronized* s, ReadLockType_&& readLock)
387 , fSharedLock_{move (readLock)}
390#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
391 DbgTrace (L
"ReadableReference::CTOR -- locks (readLock)");
393 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
394 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
397 template <
typename T,
typename TRAITS>
403#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
404 DbgTrace (L
"ReadableReference::CTOR -- locks (fSharedLock_ with mutex_=%p)", &s->fMutex_);
406 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
407 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
409 _NoteLockStateChanged (L
"ReadableReference Locked");
411 template <
typename T,
typename TRAITS>
414 , fSharedLock_{
std::move (src.fSharedLock_)}
416 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
417 this->fDbgTraceLocksName = move (src.fDbgTraceLocksName);
419 _NoteLockStateChanged (L
"ReadableReference move-Locked");
420#if qStroika_Foundation_Debug_AssertionsChecked
425 template <
typename T,
typename TRAITS>
428#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
429 DbgTrace (L
"ReadableReference::DTOR -- locks (fSharedLock_ mutex_=%p)", fSharedLock_);
431 if (fSharedLock_.owns_lock ()) {
432 _NoteLockStateChanged (L
"ReadableReference Unlocked");
435 template <
typename T,
typename TRAITS>
441 template <
typename T,
typename TRAITS>
447 template <
typename T,
typename TRAITS>
453 template <
typename T,
typename TRAITS>
459 template <
typename T,
typename TRAITS>
462 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
463 if (this->fDbgTraceLocksName) {
464#if qStroika_Foundation_Debug_DefaultTracingOn
465 Private_::DbgTraceHelper_ (m, this->fDbgTraceLocksName);
476 template <
typename T,
typename TRAITS>
477 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s)
479 , fWriteLock_{s->fMutex_}
482 this->_NoteLockStateChanged (L
"WritableReference Locked");
483 ++s->fWriteLockCount_;
485 template <
typename T,
typename TRAITS>
486 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s, WriteLockType_&& writeLock)
488 , fWriteLock_{
std::move (writeLock)}
491 this->_NoteLockStateChanged (L
"WritableReference move-Locked");
492 ++s->fWriteLockCount_;
495 template <
typename T,
typename TRAITS>
496 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s, Time::DurationSeconds timeout)
498 , fWriteLock_{s->fMutex_, timeout}
501 if (not fWriteLock_.owns_lock ()) [[unlikely]] {
502 Execution::ThrowTimeOutException ();
504 this->_NoteLockStateChanged (L
"WritableReference Locked");
505 ++s->fWriteLockCount_;
507 template <
typename T,
typename TRAITS>
508 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (WritableReference&& src)
509 : ReadableReference{
std::move (src)}
510 , fWriteLock_{
std::move (src.fWriteLock_)}
513 this->_NoteLockStateChanged (L
"WritableReference move-Locked");
515 template <
typename T,
typename TRAITS>
516 inline auto Synchronized<T, TRAITS>::WritableReference::operator= (T rhs) ->
const WritableReference&
520 *
const_cast<T*
> (this->fT) = rhs;
523 template <
typename T,
typename TRAITS>
524 inline T* Synchronized<T, TRAITS>::WritableReference::operator->()
528 return const_cast<T*
> (this->fT);
530 template <
typename T,
typename TRAITS>
533 return ReadableReference::operator->();
535 template <
typename T,
typename TRAITS>
536 inline T& Synchronized<T, TRAITS>::WritableReference::rwref ()
540 return *
const_cast<T*
> (this->fT);
542 template <
typename T,
typename TRAITS>
543 inline void Synchronized<T, TRAITS>::WritableReference::store (
const T& v)
547 template <
typename T,
typename TRAITS>
548 inline void Synchronized<T, TRAITS>::WritableReference::store (T&& v)
550 rwref () = std::move (v);
558 template <
typename T,
typename TRAITS>
559 inline auto operator^ (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} ^ T{})
561 return lhs.load () ^ rhs;
563 template <
typename T,
typename TRAITS>
566 return lhs ^ rhs.load ();
568 template <
typename T,
typename TRAITS>
569 inline auto operator^ (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} ^ T{})
572 auto l = lhs.load ();
573 auto r = rhs.load ();
582 template <
typename T,
typename TRAITS>
583 inline auto operator* (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} * T{})
585 return lhs.load () * rhs;
587 template <
typename T,
typename TRAITS>
588 inline auto operator* (T lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} * T{})
590 return lhs * rhs.load ();
592 template <
typename T,
typename TRAITS>
593 inline auto operator* (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} * T{})
596 auto l = lhs.load ();
597 auto r = rhs.load ();
606 template <
typename T,
typename TRAITS>
607 inline auto operator+ (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} + T{})
609 return lhs.load () + rhs;
611 template <
typename T,
typename TRAITS>
612 inline auto operator+ (T lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} + T{})
614 return lhs + rhs.load ();
616 template <
typename T,
typename TRAITS>
620 auto l = lhs.load ();
621 auto r = rhs.load ();
630 template <
typename T,
typename TRAITS,
typename RHSTYPE>
631 inline auto operator-= (Synchronized<T, TRAITS>& lhs, RHSTYPE rhs) ->
decltype (lhs.rwget ()->operator-= (rhs))
633 return lhs.rwget ()->operator-= (rhs);
641 template <
typename T,
typename TRAITS,
typename RHSTYPE>
642 inline auto operator+= (
Synchronized<T, TRAITS>& lhs, RHSTYPE rhs) ->
decltype (lhs.rwget ()->operator+= (rhs))
644 return lhs.rwget ()->operator+= (rhs);
652 template <
typename T,
typename TRAITS>
655 return lhs.load () + rhs;
657 template <
typename T,
typename TRAITS>
660 return lhs - rhs.load ();
662 template <
typename T,
typename TRAITS>
663 inline auto operator- (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} - T{})
666 auto l = lhs.load ();
667 auto r = rhs.load ();
#define RequireNotNull(p)
#define RequireExpression(c)
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
ReadableReference(const Synchronized *s)
nonvirtual T load() const
more or less identical to cref () - except return value is value, not reference.
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
nonvirtual WritableReference rwget()
get a read-write smart pointer to the underlying Synchronized<> object, holding the full lock the who...
nonvirtual void store(const T &v)
nonvirtual bool try_lock() const
nonvirtual void UpgradeLockNonAtomically(ReadableReference *lockBeingUpgraded, const function< void(WritableReference &&)> &doWithWriteLock, Time::DurationSeconds timeout=Time::kInfinity)
Same as UpgradeLockNonAtomicallyQuietly, but throws on failure (either timeout or if argument functio...
nonvirtual bool UpgradeLockNonAtomicallyQuietly(ReadableReference *lockBeingUpgraded, const function< void(WritableReference &&)> &doWithWriteLock, Time::DurationSeconds timeout=Time::kInfinity)
Upgrade a shared_lock (ReadableReference) to a (WritableReference) full lock temporarily in the conte...
nonvirtual bool try_lock_for(const chrono::duration< double > &tryFor) const
nonvirtual void lock() const
nonvirtual ReadableReference cget() const
get a read-only smart pointer to the underlying Synchronized<> object, holding the readlock the whole...
Synchronized(ARGUMENT_TYPES &&... args)
nonvirtual void unlock() const
nonvirtual void lock_shared() const
nonvirtual void unlock_shared() const
nonvirtual T load() const
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
void ThrowTimeOutException()
Execution::Throw (Execution::TimeOutException::kThe); but can be more easily forward-declared,...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >