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 : fProtectedValue_ (forward<ARGUMENT_TYPES> (args)...)
34 template <
typename T,
typename TRAITS>
36 : fProtectedValue_{src.cget ().load ()}
39 template <
typename T,
typename TRAITS>
40 inline auto Synchronized<T, TRAITS>::operator= (
const Synchronized& rhs) -> Synchronized&
42 if (&rhs !=
this) [[likely]] {
43 auto value = rhs.cget ().
load ();
44 [[maybe_unused]] lock_guard critSec{fMutex_};
45 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
46 NoteLockStateChanged_ (L
"Locked");
48 fProtectedValue_ = value;
52 template <
typename T,
typename TRAITS>
53 inline auto Synchronized<T, TRAITS>::operator= (T&& rhs) -> Synchronized&
55 [[maybe_unused]] lock_guard critSec{fMutex_};
56 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
57 NoteLockStateChanged_ (L
"Locked");
58 fProtectedValue_ = move (rhs);
62 template <
typename T,
typename TRAITS>
63 inline auto Synchronized<T, TRAITS>::operator= (
const T& rhs) -> Synchronized&
65 [[maybe_unused]] lock_guard critSec{fMutex_};
66 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
67 NoteLockStateChanged_ (L
"Locked");
68 fProtectedValue_ = rhs;
72 template <
typename T,
typename TRAITS>
74 requires (TRAITS::kIsRecursiveReadMutex)
78 template <
typename T,
typename TRAITS>
80 requires (TRAITS::kIsRecursiveReadMutex)
82 ReadLockType_ fromCritSec{fMutex_};
83 return fProtectedValue_;
85 template <
typename T,
typename TRAITS>
87 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportsTimedLocks)
89 ReadLockType_ critSec{fMutex_, tryFor};
90 if (not critSec) [[unlikely]] {
93 return fProtectedValue_;
95 template <
typename T,
typename TRAITS>
97 requires (TRAITS::kIsRecursiveLockMutex)
99 [[maybe_unused]] lock_guard critSec{fMutex_};
100 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
101 NoteLockStateChanged_ (L
"Locked");
103 fProtectedValue_ = v;
105 template <
typename T,
typename TRAITS>
107 requires (TRAITS::kIsRecursiveLockMutex)
109 [[maybe_unused]] lock_guard critSec{fMutex_};
110 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
111 NoteLockStateChanged_ (L
"Locked");
113 fProtectedValue_ = std::move (v);
115 template <
typename T,
typename TRAITS>
117 requires (TRAITS::kIsRecursiveLockMutex and TRAITS::kSupportsTimedLocks)
119 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
120 if (not critSec) [[unlikely]] {
123 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
124 NoteLockStateChanged_ (L
"Locked");
126 fProtectedValue_ = v;
128 template <
typename T,
typename TRAITS>
130 requires (TRAITS::kIsRecursiveLockMutex and TRAITS::kSupportsTimedLocks)
132 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
133 if (not critSec) [[unlikely]] {
136 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([
this] ()
noexcept { NoteLockStateChanged_ (L
"Unlocked"); });
137 NoteLockStateChanged_ (L
"Locked");
139 fProtectedValue_ = std::move (v);
141 template <
typename T,
typename TRAITS>
146 template <
typename T,
typename TRAITS>
148 requires (TRAITS::kSupportsTimedLocks)
150 ReadLockType_ critSec{fMutex_, tryFor};
151 if (not critSec) [[unlikely]] {
154 return ReadableReference{
this, move (critSec)};
156 template <
typename T,
typename TRAITS>
159 return WritableReference{
this};
161 template <
typename T,
typename TRAITS>
163 requires (TRAITS::kSupportsTimedLocks)
165 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
166 if (not critSec) [[unlikely]] {
169 return WritableReference{
this, move (critSec)};
171 template <
typename T,
typename TRAITS>
172 inline auto Synchronized<T, TRAITS>::operator->() const -> ReadableReference
174 return ReadableReference{
this};
176 template <
typename T,
typename TRAITS>
178 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportSharedLocks)
180#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
183 fMutex_.lock_shared ();
184 NoteLockStateChanged_ (L
"Locked Shared");
186 template <
typename T,
typename TRAITS>
188 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportSharedLocks)
190#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
193 fMutex_.unlock_shared ();
194 NoteLockStateChanged_ (L
"Unlocked Shared");
196 template <
typename T,
typename TRAITS>
198 requires (TRAITS::kIsRecursiveReadMutex)
200#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
204 NoteLockStateChanged_ (L
"Locked");
207 template <
typename T,
typename TRAITS>
209 requires (TRAITS::kIsRecursiveReadMutex)
211#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
214 bool result = fMutex_.try_lock ();
215 if (result) [[likely]] {
216 NoteLockStateChanged_ (L
"Locked");
221 template <
typename T,
typename TRAITS>
223 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportsTimedLocks)
225#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
228 bool result = fMutex_.try_lock_for (tryFor);
229 if (result) [[likely]] {
230 NoteLockStateChanged_ (L
"Locked");
235 template <
typename T,
typename TRAITS>
237 requires (TRAITS::kIsRecursiveReadMutex)
239#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
242 NoteLockStateChanged_ (L
"Unlocked");
245 template <
typename T,
typename TRAITS>
247 requires (TRAITS::kIsRecursiveReadMutex and equality_comparable<T>)
249 return load () == rhs.
load ();
251 template <
typename T,
typename TRAITS>
252 inline bool Synchronized<T, TRAITS>::operator== (
const T& rhs)
const
253 requires (TRAITS::kIsRecursiveReadMutex and equality_comparable<T>)
255 return load () == rhs;
257 template <
typename T,
typename TRAITS>
258 inline auto Synchronized<T, TRAITS>::operator<=> (
const Synchronized& rhs)
const
259 requires (TRAITS::kIsRecursiveReadMutex and three_way_comparable<T>)
261 return load () <=> rhs.load ();
263 template <
typename T,
typename TRAITS>
264 inline auto Synchronized<T, TRAITS>::operator<=> (
const T& rhs)
const
265 requires (TRAITS::kIsRecursiveReadMutex and three_way_comparable<T>)
267 return load () <=> rhs;
269 template <
typename T,
typename TRAITS>
271 const function<
void (WritableReference&&)>& doWithWriteLock,
273 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
275 return UpgradeLockNonAtomicallyQuietly (
277 [&] (WritableReference&& wRef, [[maybe_unused]]
bool interveningWriteLock) {
278 if (interveningWriteLock) [[unlikely]] {
279#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
280 DbgTrace (
"in UpgradeLockNonAtomicallyQuietly - turning interveningWriteLock into fake timeout");
285 doWithWriteLock (std::move (wRef));
291 template <
typename T,
typename TRAITS>
293 const function<
bool (WritableReference&&,
bool interveningWriteLock)>& doWithWriteLock,
295 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
298#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
299 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
303 Require (lockBeingUpgraded->fSharedLock_.mutex () == &fMutex_);
304 Require (lockBeingUpgraded->fSharedLock_.owns_lock ());
305 auto writeLockCountBeforeReleasingReadLock = fWriteLockCount_;
306 fMutex_.unlock_shared ();
308 fMutex_.lock_shared ();
309 NoteLockStateChanged_ (L
"in Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly finally relocked shared");
311 typename TRAITS::WriteLockType upgradeLock{fMutex_, std::defer_lock};
312 if (timeout >= Time::DurationSeconds::max ()) {
316 if (not upgradeLock.try_lock_for (timeout)) {
320 NoteLockStateChanged_ (L
"in Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly acquired Lock");
321 WritableReference wr = WritableReference{
this, std::move (upgradeLock)};
322 bool interveningWriteLock = fWriteLockCount_ > 1 + writeLockCountBeforeReleasingReadLock;
324 return doWithWriteLock (std::move (wr), interveningWriteLock);
326 template <
typename T,
typename TRAITS>
328 const function<
void (WritableReference&&)>& doWithWriteLock,
330 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
332#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
333 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
336 if (not UpgradeLockNonAtomicallyQuietly (lockBeingUpgraded, doWithWriteLock, timeout)) [[unlikely]] {
340 template <
typename T,
typename TRAITS>
342 const function<
bool (WritableReference&&,
bool interveningWriteLock)>& doWithWriteLock,
344 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
346#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
347 Debug::TraceContextBumper ctx{L
"Synchronized<T, TRAITS>::UpgradeLockNonAtomically", L
"&fMutex_=%p, timeout=%s", &fMutex_,
350 if (not UpgradeLockNonAtomicallyQuietly (lockBeingUpgraded, doWithWriteLock, timeout)) [[unlikely]] {
354 template <
typename T,
typename TRAITS>
355 inline void Synchronized<T, TRAITS>::NoteLockStateChanged_ ([[maybe_unused]]
const wchar_t* m)
const noexcept
357 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
358 if (this->fDbgTraceLocksName) {
359#if qStroika_Foundation_Debug_DefaultTracingOn
360 Private_::DbgTraceHelper_ (m, this->fDbgTraceLocksName);
371 template <
typename T,
typename TRAITS>
376#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
377 DbgTrace (L
"ReadableReference::CTOR -- locks (_eExternallyLocked)");
379 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
380 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
383 template <
typename T,
typename TRAITS>
384 inline Synchronized<T, TRAITS>::ReadableReference::ReadableReference (
const Synchronized* s, ReadLockType_&& readLock)
386 , fSharedLock_{move (readLock)}
389#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
390 DbgTrace (L
"ReadableReference::CTOR -- locks (readLock)");
392 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
393 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
396 template <
typename T,
typename TRAITS>
402#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
403 DbgTrace (L
"ReadableReference::CTOR -- locks (fSharedLock_ with mutex_=%p)", &s->fMutex_);
405 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
406 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
408 _NoteLockStateChanged (L
"ReadableReference Locked");
410 template <
typename T,
typename TRAITS>
413 , fSharedLock_{
std::move (src.fSharedLock_)}
415 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
416 this->fDbgTraceLocksName = move (src.fDbgTraceLocksName);
418 _NoteLockStateChanged (L
"ReadableReference move-Locked");
419#if qStroika_Foundation_Debug_AssertionsChecked
424 template <
typename T,
typename TRAITS>
427#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
428 DbgTrace (L
"ReadableReference::DTOR -- locks (fSharedLock_ mutex_=%p)", fSharedLock_);
430 if (fSharedLock_.owns_lock ()) {
431 _NoteLockStateChanged (L
"ReadableReference Unlocked");
434 template <
typename T,
typename TRAITS>
440 template <
typename T,
typename TRAITS>
446 template <
typename T,
typename TRAITS>
452 template <
typename T,
typename TRAITS>
458 template <
typename T,
typename TRAITS>
461 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
462 if (this->fDbgTraceLocksName) {
463#if qStroika_Foundation_Debug_DefaultTracingOn
464 Private_::DbgTraceHelper_ (m, this->fDbgTraceLocksName);
475 template <
typename T,
typename TRAITS>
476 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s)
478 , fWriteLock_{s->fMutex_}
481 this->_NoteLockStateChanged (L
"WritableReference Locked");
482 ++s->fWriteLockCount_;
484 template <
typename T,
typename TRAITS>
485 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s, WriteLockType_&& writeLock)
487 , fWriteLock_{
std::move (writeLock)}
490 this->_NoteLockStateChanged (L
"WritableReference move-Locked");
491 ++s->fWriteLockCount_;
494 template <
typename T,
typename TRAITS>
495 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s, Time::DurationSeconds timeout)
497 , fWriteLock_{s->fMutex_, timeout}
500 if (not fWriteLock_.owns_lock ()) [[unlikely]] {
501 Execution::ThrowTimeOutException ();
503 this->_NoteLockStateChanged (L
"WritableReference Locked");
504 ++s->fWriteLockCount_;
506 template <
typename T,
typename TRAITS>
507 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (WritableReference&& src)
508 : ReadableReference{
std::move (src)}
509 , fWriteLock_{
std::move (src.fWriteLock_)}
512 this->_NoteLockStateChanged (L
"WritableReference move-Locked");
514 template <
typename T,
typename TRAITS>
515 inline auto Synchronized<T, TRAITS>::WritableReference::operator= (T rhs) ->
const WritableReference&
519 *
const_cast<T*
> (this->fT) = rhs;
522 template <
typename T,
typename TRAITS>
523 inline T* Synchronized<T, TRAITS>::WritableReference::operator->()
527 return const_cast<T*
> (this->fT);
529 template <
typename T,
typename TRAITS>
532 return ReadableReference::operator->();
534 template <
typename T,
typename TRAITS>
535 inline T& Synchronized<T, TRAITS>::WritableReference::rwref ()
539 return *
const_cast<T*
> (this->fT);
541 template <
typename T,
typename TRAITS>
542 inline void Synchronized<T, TRAITS>::WritableReference::store (
const T& v)
546 template <
typename T,
typename TRAITS>
547 inline void Synchronized<T, TRAITS>::WritableReference::store (T&& v)
549 rwref () = std::move (v);
557 template <
typename T,
typename TRAITS>
558 inline auto operator^ (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} ^ T{})
560 return lhs.load () ^ rhs;
562 template <
typename T,
typename TRAITS>
565 return lhs ^ rhs.load ();
567 template <
typename T,
typename TRAITS>
568 inline auto operator^ (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} ^ T{})
571 auto l = lhs.load ();
572 auto r = rhs.load ();
581 template <
typename T,
typename TRAITS>
582 inline auto operator* (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} * T{})
584 return lhs.load () * rhs;
586 template <
typename T,
typename TRAITS>
587 inline auto operator* (T lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} * T{})
589 return lhs * rhs.load ();
591 template <
typename T,
typename TRAITS>
592 inline auto operator* (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} * T{})
595 auto l = lhs.load ();
596 auto r = rhs.load ();
605 template <
typename T,
typename TRAITS>
606 inline auto operator+ (
const Synchronized<T, TRAITS>& lhs, T rhs) ->
decltype (T{} + T{})
608 return lhs.load () + rhs;
610 template <
typename T,
typename TRAITS>
611 inline auto operator+ (T lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} + T{})
613 return lhs + rhs.load ();
615 template <
typename T,
typename TRAITS>
619 auto l = lhs.load ();
620 auto r = rhs.load ();
629 template <
typename T,
typename TRAITS,
typename RHSTYPE>
630 inline auto operator-= (Synchronized<T, TRAITS>& lhs, RHSTYPE rhs) ->
decltype (lhs.rwget ()->operator-= (rhs))
632 return lhs.rwget ()->operator-= (rhs);
640 template <
typename T,
typename TRAITS,
typename RHSTYPE>
641 inline auto operator+= (
Synchronized<T, TRAITS>& lhs, RHSTYPE rhs) ->
decltype (lhs.rwget ()->operator+= (rhs))
643 return lhs.rwget ()->operator+= (rhs);
651 template <
typename T,
typename TRAITS>
654 return lhs.load () + rhs;
656 template <
typename T,
typename TRAITS>
659 return lhs - rhs.load ();
661 template <
typename T,
typename TRAITS>
662 inline auto operator- (
const Synchronized<T, TRAITS>& lhs,
const Synchronized<T, TRAITS>& rhs) ->
decltype (T{} - T{})
665 auto l = lhs.load ();
666 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...
Synchronized(ARGUMENT_TYPES &&... args)
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...
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 >