13#if qStroika_Foundation_Debug_DefaultTracingOn
14 void DbgTraceHelper_ (
const wchar_t* w1,
const optional<std::wstring>& w2);
21 [[noreturn]]
void ThrowTimeout_ ();
32 template <
typename T,
typename TRAITS>
33 template <
typename... ARGUMENT_TYPES>
35 requires (constructible_from<T, ARGUMENT_TYPES...>)
36 : fProtectedValue_ (forward<ARGUMENT_TYPES> (args)...)
39 template <
typename T,
typename TRAITS>
41 : fProtectedValue_{src.cget ().load ()}
44 template <
typename T,
typename TRAITS>
45 inline auto Synchronized<T, TRAITS>::operator= (
const Synchronized& rhs) -> Synchronized&
47 if (&rhs !=
this) [[likely]] {
48 auto value = rhs.cget ().
load ();
49 [[maybe_unused]] lock_guard critSec{fMutex_};
50 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
51 NoteLockStateChanged_ (L
"Locked");
53 fProtectedValue_ = value;
57 template <
typename T,
typename TRAITS>
58 inline auto Synchronized<T, TRAITS>::operator= (T&& rhs) -> Synchronized&
60 [[maybe_unused]] lock_guard critSec{fMutex_};
61 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
62 NoteLockStateChanged_ (L
"Locked");
63 fProtectedValue_ = move (rhs);
67 template <
typename T,
typename TRAITS>
68 inline auto Synchronized<T, TRAITS>::operator= (
const T& rhs) -> Synchronized&
70 [[maybe_unused]] lock_guard critSec{fMutex_};
71 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
72 NoteLockStateChanged_ (L
"Locked");
73 fProtectedValue_ = rhs;
77 template <
typename T,
typename TRAITS>
79 requires (TRAITS::kIsRecursiveReadMutex)
83 template <
typename T,
typename TRAITS>
85 requires (TRAITS::kIsRecursiveReadMutex)
87 ReadLockType_ fromCritSec{fMutex_};
88 return fProtectedValue_;
90 template <
typename T,
typename TRAITS>
92 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportsTimedLocks)
94 ReadLockType_ critSec{fMutex_, tryFor};
95 if (not critSec) [[unlikely]] {
96 Private_::ThrowTimeout_ ();
98 return fProtectedValue_;
100 template <
typename T,
typename TRAITS>
102 requires (TRAITS::kIsRecursiveLockMutex)
104 [[maybe_unused]] lock_guard critSec{fMutex_};
105 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
106 NoteLockStateChanged_ (L
"Locked");
108 fProtectedValue_ = v;
110 template <
typename T,
typename TRAITS>
112 requires (TRAITS::kIsRecursiveLockMutex)
114 [[maybe_unused]] lock_guard critSec{fMutex_};
115 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
116 NoteLockStateChanged_ (L
"Locked");
118 fProtectedValue_ = std::move (v);
120 template <
typename T,
typename TRAITS>
122 requires (TRAITS::kIsRecursiveLockMutex and TRAITS::kSupportsTimedLocks)
124 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
125 if (not critSec) [[unlikely]] {
126 Private_::ThrowTimeout_ ();
128 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
129 NoteLockStateChanged_ (L
"Locked");
131 fProtectedValue_ = v;
133 template <
typename T,
typename TRAITS>
135 requires (TRAITS::kIsRecursiveLockMutex and TRAITS::kSupportsTimedLocks)
137 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
138 if (not critSec) [[unlikely]] {
139 Private_::ThrowTimeout_ ();
141 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
142 NoteLockStateChanged_ (L
"Locked");
144 fProtectedValue_ = std::move (v);
146 template <
typename T,
typename TRAITS>
151 template <
typename T,
typename TRAITS>
153 requires (TRAITS::kSupportsTimedLocks)
155 ReadLockType_ critSec{fMutex_, tryFor};
156 if (not critSec) [[unlikely]] {
157 Private_::ThrowTimeout_ ();
159 return ReadableReference{
this, move (critSec)};
161 template <
typename T,
typename TRAITS>
164 return WritableReference{
this};
166 template <
typename T,
typename TRAITS>
168 requires (TRAITS::kSupportsTimedLocks)
170 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
171 if (not critSec) [[unlikely]] {
172 Private_::ThrowTimeout_ ();
174 return WritableReference{
this, move (critSec)};
176 template <
typename T,
typename TRAITS>
177 inline auto Synchronized<T, TRAITS>::operator->() const -> ReadableReference
179 return ReadableReference{
this};
181 template <
typename T,
typename TRAITS>
183 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportSharedLocks)
185#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
188 fMutex_.lock_shared ();
189 NoteLockStateChanged_ (L
"Locked Shared");
191 template <
typename T,
typename TRAITS>
193 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportSharedLocks)
195#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
198 fMutex_.unlock_shared ();
199 NoteLockStateChanged_ (L
"Unlocked Shared");
201 template <
typename T,
typename TRAITS>
203 requires (TRAITS::kIsRecursiveReadMutex)
205#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
209 NoteLockStateChanged_ (L
"Locked");
212 template <
typename T,
typename TRAITS>
214 requires (TRAITS::kIsRecursiveReadMutex)
216#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
219 bool result = fMutex_.try_lock ();
220 if (result) [[likely]] {
221 NoteLockStateChanged_ (L
"Locked");
226 template <
typename T,
typename TRAITS>
228 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportsTimedLocks)
230#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
233 bool result = fMutex_.try_lock_for (tryFor);
234 if (result) [[likely]] {
235 NoteLockStateChanged_ (L
"Locked");
240 template <
typename T,
typename TRAITS>
242 requires (TRAITS::kIsRecursiveReadMutex)
244#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
247 NoteLockStateChanged_ (L
"Unlocked");
250 template <
typename T,
typename TRAITS>
252 requires (TRAITS::kIsRecursiveReadMutex and equality_comparable<T>)
254 return load () == rhs.
load ();
256 template <
typename T,
typename TRAITS>
257 inline bool Synchronized<T, TRAITS>::operator== (
const T& rhs)
const
258 requires (TRAITS::kIsRecursiveReadMutex and equality_comparable<T>)
260 return load () == rhs;
262 template <
typename T,
typename TRAITS>
263 inline auto Synchronized<T, TRAITS>::operator<=> (
const Synchronized& rhs)
const
264 requires (TRAITS::kIsRecursiveReadMutex and three_way_comparable<T>)
266 return load () <=> rhs.load ();
268 template <
typename T,
typename TRAITS>
269 inline auto Synchronized<T, TRAITS>::operator<=> (
const T& rhs)
const
270 requires (TRAITS::kIsRecursiveReadMutex and three_way_comparable<T>)
272 return load () <=> rhs;
274 template <
typename T,
typename TRAITS>
276 const function<
void (WritableReference&&)>& doWithWriteLock,
278 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
280 return UpgradeLockNonAtomicallyQuietly (
282 [&] (WritableReference&& wRef, [[maybe_unused]]
bool interveningWriteLock) {
283 if (interveningWriteLock) [[unlikely]] {
284#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
285 DbgTrace (
"in UpgradeLockNonAtomicallyQuietly - turning interveningWriteLock into fake timeout");
290 doWithWriteLock (std::move (wRef));
296 template <
typename T,
typename TRAITS>
298 const function<
bool (WritableReference&&,
bool interveningWriteLock)>& doWithWriteLock,
300 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
303#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
304 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
308 Require (lockBeingUpgraded->fSharedLock_.mutex () == &fMutex_);
309 Require (lockBeingUpgraded->fSharedLock_.owns_lock ());
310 auto writeLockCountBeforeReleasingReadLock = fWriteLockCount_;
311 fMutex_.unlock_shared ();
313 fMutex_.lock_shared ();
314 NoteLockStateChanged_ (L
"in Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly finally relocked shared");
316 typename TRAITS::WriteLockType upgradeLock{fMutex_, std::defer_lock};
317 if (timeout >= Time::DurationSeconds::max ()) {
321 if (not upgradeLock.try_lock_for (timeout)) {
325 NoteLockStateChanged_ (L
"in Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly acquired Lock");
326 WritableReference wr = WritableReference{
this, std::move (upgradeLock)};
327 bool interveningWriteLock = fWriteLockCount_ > 1 + writeLockCountBeforeReleasingReadLock;
329 return doWithWriteLock (std::move (wr), interveningWriteLock);
331 template <
typename T,
typename TRAITS>
333 const function<
void (WritableReference&&)>& doWithWriteLock,
335 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
337#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
338 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
341 if (not UpgradeLockNonAtomicallyQuietly (lockBeingUpgraded, doWithWriteLock, timeout)) [[unlikely]] {
342 Execution::Private_::ThrowTimeout_ ();
345 template <
typename T,
typename TRAITS>
347 const function<
bool (WritableReference&&,
bool interveningWriteLock)>& doWithWriteLock,
349 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
351#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
352 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
355 if (not UpgradeLockNonAtomicallyQuietly (lockBeingUpgraded, doWithWriteLock, timeout)) [[unlikely]] {
356 Execution::Private_::ThrowTimeout_ ();
359 template <
typename T,
typename TRAITS>
360 inline void Synchronized<T, TRAITS>::NoteLockStateChanged_ ([[maybe_unused]]
const wchar_t* m)
const noexcept
362 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
363 if (this->fDbgTraceLocksName) {
364#if qStroika_Foundation_Debug_DefaultTracingOn
365 Private_::DbgTraceHelper_ (m, this->fDbgTraceLocksName);
376 template <
typename T,
typename TRAITS>
381#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
382 DbgTrace (L
"ReadableReference::CTOR -- locks (_eExternallyLocked)");
384 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
385 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
388 template <
typename T,
typename TRAITS>
389 inline Synchronized<T, TRAITS>::ReadableReference::ReadableReference (
const Synchronized* s, ReadLockType_&& readLock)
391 , fSharedLock_{move (readLock)}
394#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
395 DbgTrace (L
"ReadableReference::CTOR -- locks (readLock)");
397 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
398 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
401 template <
typename T,
typename TRAITS>
407#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
408 DbgTrace (L
"ReadableReference::CTOR -- locks (fSharedLock_ with mutex_=%p)", &s->fMutex_);
410 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
411 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
413 _NoteLockStateChanged (L
"ReadableReference Locked");
415 template <
typename T,
typename TRAITS>
418 , fSharedLock_{
std::move (src.fSharedLock_)}
420 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
421 this->fDbgTraceLocksName = move (src.fDbgTraceLocksName);
423 _NoteLockStateChanged (L
"ReadableReference move-Locked");
424#if qStroika_Foundation_Debug_AssertionsChecked
429 template <
typename T,
typename TRAITS>
432#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
433 DbgTrace (L
"ReadableReference::DTOR -- locks (fSharedLock_ mutex_=%p)", fSharedLock_);
435 if (fSharedLock_.owns_lock ()) {
436 _NoteLockStateChanged (L
"ReadableReference Unlocked");
439 template <
typename T,
typename TRAITS>
445 template <
typename T,
typename TRAITS>
451 template <
typename T,
typename TRAITS>
457 template <
typename T,
typename TRAITS>
463 template <
typename T,
typename TRAITS>
466 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
467 if (this->fDbgTraceLocksName) {
468#if qStroika_Foundation_Debug_DefaultTracingOn
469 Private_::DbgTraceHelper_ (m, this->fDbgTraceLocksName);
480 template <
typename T,
typename TRAITS>
481 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s)
483 , fWriteLock_{s->fMutex_}
486 this->_NoteLockStateChanged (L
"WritableReference Locked");
487 ++s->fWriteLockCount_;
489 template <
typename T,
typename TRAITS>
490 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s, WriteLockType_&& writeLock)
492 , fWriteLock_{
std::move (writeLock)}
495 this->_NoteLockStateChanged (L
"WritableReference move-Locked");
496 ++s->fWriteLockCount_;
499 template <
typename T,
typename TRAITS>
500 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s, Time::DurationSeconds timeout)
502 , fWriteLock_{s->fMutex_, timeout}
505 if (not fWriteLock_.owns_lock ()) [[unlikely]] {
506 Execution::Private_::ThrowTimeout_ ();
508 this->_NoteLockStateChanged (L
"WritableReference Locked");
509 ++s->fWriteLockCount_;
511 template <
typename T,
typename TRAITS>
512 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (WritableReference&& src)
513 : ReadableReference{
std::move (src)}
514 , fWriteLock_{
std::move (src.fWriteLock_)}
517 this->_NoteLockStateChanged (L
"WritableReference move-Locked");
519 template <
typename T,
typename TRAITS>
520 inline auto Synchronized<T, TRAITS>::WritableReference::operator= (T rhs) ->
const WritableReference&
524 *
const_cast<T*
> (this->fT) = rhs;
527 template <
typename T,
typename TRAITS>
528 inline T* Synchronized<T, TRAITS>::WritableReference::operator->()
532 return const_cast<T*
> (this->fT);
534 template <
typename T,
typename TRAITS>
537 return ReadableReference::operator->();
539 template <
typename T,
typename TRAITS>
540 inline T& Synchronized<T, TRAITS>::WritableReference::rwref ()
544 return *
const_cast<T*
> (this->fT);
546 template <
typename T,
typename TRAITS>
547 inline void Synchronized<T, TRAITS>::WritableReference::store (
const T& v)
551 template <
typename T,
typename TRAITS>
552 inline void Synchronized<T, TRAITS>::WritableReference::store (T&& v)
554 rwref () = std::move (v);
562 template <
typename T,
typename TRAITS>
563 inline auto operator^ (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} ^ T{})
565 return lhs.load () ^ rhs;
567 template <
typename T,
typename TRAITS>
570 return lhs ^ rhs.load ();
572 template <
typename T,
typename TRAITS>
573 inline auto operator^ (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} ^ T{})
576 auto l = lhs.load ();
577 auto r = rhs.load ();
586 template <
typename T,
typename TRAITS>
587 inline auto operator* (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} * T{})
589 return lhs.load () * rhs;
591 template <
typename T,
typename TRAITS>
592 inline auto operator* (T lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} * T{})
594 return lhs * rhs.load ();
596 template <
typename T,
typename TRAITS>
597 inline auto operator* (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} * T{})
600 auto l = lhs.load ();
601 auto r = rhs.load ();
610 template <
typename T,
typename TRAITS>
611 inline auto operator+ (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} + T{})
613 return lhs.load () + rhs;
615 template <
typename T,
typename TRAITS>
616 inline auto operator+ (T lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} + T{})
618 return lhs + rhs.load ();
620 template <
typename T,
typename TRAITS>
624 auto l = lhs.load ();
625 auto r = rhs.load ();
634 template <
typename T,
typename TRAITS,
typename RHSTYPE>
635 inline auto operator-= (Synchronized<T, TRAITS>& lhs, RHSTYPE rhs) ->
decltype (lhs.rwget ()->operator-= (rhs))
637 return lhs.rwget ()->operator-= (rhs);
645 template <
typename T,
typename TRAITS,
typename RHSTYPE>
646 inline auto operator+= (
Synchronized<T, TRAITS>& lhs, RHSTYPE rhs) ->
decltype (lhs.rwget ()->operator+= (rhs))
648 return lhs.rwget ()->operator+= (rhs);
656 template <
typename T,
typename TRAITS>
659 return lhs.load () + rhs;
661 template <
typename T,
typename TRAITS>
664 return lhs - rhs.load ();
666 template <
typename T,
typename TRAITS>
667 inline auto operator- (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} - T{})
670 auto l = lhs.load ();
671 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...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >