Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Synchronized.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4
5// Comment this in to turn on aggressive noisy DbgTrace in this module (note the extra long name since its in a header)
6//#define Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_ 1
7
10
12 namespace Private_ {
13#if qStroika_Foundation_Debug_DefaultTracingOn
14 void DbgTraceHelper_ (const wchar_t* w1, const optional<std::wstring>& w2);
15#endif
16 /**
17 * Equivalent to Execution::ThrowError (errc::timed_out), but declarable HERE - Synchronized.h cannot
18 * include Exceptions.h (deadly embrace), and ThrowError () is inline, so forward-declaring it would
19 * compile and then fail to link. Defined out-of-line in Synchronized.cpp, like DbgTraceHelper_ above.
20 */
21 [[noreturn]] void ThrowTimeout_ ();
22 }
23}
24
26
27 /*
28 ********************************************************************************
29 **************************** Synchronized<T, TRAITS> ***************************
30 ********************************************************************************
31 */
32 template <typename T, typename TRAITS>
33 template <typename... ARGUMENT_TYPES>
34 inline Synchronized<T, TRAITS>::Synchronized (ARGUMENT_TYPES&&... args)
35 requires (constructible_from<T, ARGUMENT_TYPES...>)
36 : fProtectedValue_ (forward<ARGUMENT_TYPES> (args)...) // use () not {} so works with T with explicit CTOR (empirically has trouble - not sure why)
37 {
38 }
39 template <typename T, typename TRAITS>
41 : fProtectedValue_{src.cget ().load ()}
42 {
43 }
44 template <typename T, typename TRAITS>
45 inline auto Synchronized<T, TRAITS>::operator= (const Synchronized& rhs) -> Synchronized&
46 {
47 if (&rhs != this) [[likely]] {
48 auto value = rhs.cget ().load (); // load outside the lock to avoid possible deadlock
49 [[maybe_unused]] lock_guard critSec{fMutex_};
50 [[maybe_unused]] auto&& cleanup = Execution::Finally ([this] () noexcept { NoteLockStateChanged_ (L"Unlocked"); });
51 NoteLockStateChanged_ (L"Locked");
52 ++fWriteLockCount_;
53 fProtectedValue_ = value;
54 }
55 return *this;
56 }
57 template <typename T, typename TRAITS>
58 inline auto Synchronized<T, TRAITS>::operator= (T&& rhs) -> Synchronized&
59 {
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);
64 ++fWriteLockCount_;
65 return *this;
66 }
67 template <typename T, typename TRAITS>
68 inline auto Synchronized<T, TRAITS>::operator= (const T& rhs) -> Synchronized&
69 {
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;
74 ++fWriteLockCount_;
75 return *this;
76 }
77 template <typename T, typename TRAITS>
79 requires (TRAITS::kIsRecursiveReadMutex)
80 {
81 return load ();
82 }
83 template <typename T, typename TRAITS>
85 requires (TRAITS::kIsRecursiveReadMutex)
86 {
87 ReadLockType_ fromCritSec{fMutex_};
88 return fProtectedValue_;
89 }
90 template <typename T, typename TRAITS>
91 inline T Synchronized<T, TRAITS>::load (const chrono::duration<double>& tryFor) const
92 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportsTimedLocks)
93 {
94 ReadLockType_ critSec{fMutex_, tryFor};
95 if (not critSec) [[unlikely]] {
96 Private_::ThrowTimeout_ ();
97 }
98 return fProtectedValue_;
99 }
100 template <typename T, typename TRAITS>
101 inline void Synchronized<T, TRAITS>::store (const T& v)
102 requires (TRAITS::kIsRecursiveLockMutex)
103 {
104 [[maybe_unused]] lock_guard critSec{fMutex_};
105 [[maybe_unused]] auto&& cleanup = Execution::Finally ([this] () noexcept { NoteLockStateChanged_ (L"Unlocked"); });
106 NoteLockStateChanged_ (L"Locked");
107 ++fWriteLockCount_;
108 fProtectedValue_ = v;
109 }
110 template <typename T, typename TRAITS>
111 inline void Synchronized<T, TRAITS>::store (T&& v)
112 requires (TRAITS::kIsRecursiveLockMutex)
113 {
114 [[maybe_unused]] lock_guard critSec{fMutex_};
115 [[maybe_unused]] auto&& cleanup = Execution::Finally ([this] () noexcept { NoteLockStateChanged_ (L"Unlocked"); });
116 NoteLockStateChanged_ (L"Locked");
117 ++fWriteLockCount_;
118 fProtectedValue_ = std::move (v);
119 }
120 template <typename T, typename TRAITS>
121 inline void Synchronized<T, TRAITS>::store (const T& v, const chrono::duration<double>& tryFor)
122 requires (TRAITS::kIsRecursiveLockMutex and TRAITS::kSupportsTimedLocks)
123 {
124 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
125 if (not critSec) [[unlikely]] {
126 Private_::ThrowTimeout_ ();
127 }
128 [[maybe_unused]] auto&& cleanup = Execution::Finally ([this] () noexcept { NoteLockStateChanged_ (L"Unlocked"); });
129 NoteLockStateChanged_ (L"Locked");
130 ++fWriteLockCount_;
131 fProtectedValue_ = v;
132 }
133 template <typename T, typename TRAITS>
134 inline void Synchronized<T, TRAITS>::store (T&& v, const chrono::duration<double>& tryFor)
135 requires (TRAITS::kIsRecursiveLockMutex and TRAITS::kSupportsTimedLocks)
136 {
137 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
138 if (not critSec) [[unlikely]] {
139 Private_::ThrowTimeout_ ();
140 }
141 [[maybe_unused]] auto&& cleanup = Execution::Finally ([this] () noexcept { NoteLockStateChanged_ (L"Unlocked"); });
142 NoteLockStateChanged_ (L"Locked");
143 ++fWriteLockCount_;
144 fProtectedValue_ = std::move (v);
145 }
146 template <typename T, typename TRAITS>
148 {
149 return ReadableReference{this};
150 }
151 template <typename T, typename TRAITS>
152 inline auto Synchronized<T, TRAITS>::cget (const chrono::duration<double>& tryFor) const -> ReadableReference
153 requires (TRAITS::kSupportsTimedLocks)
154 {
155 ReadLockType_ critSec{fMutex_, tryFor};
156 if (not critSec) [[unlikely]] {
157 Private_::ThrowTimeout_ ();
158 }
159 return ReadableReference{this, move (critSec)};
160 }
161 template <typename T, typename TRAITS>
162 inline auto Synchronized<T, TRAITS>::rwget () -> WritableReference
163 {
164 return WritableReference{this};
165 }
166 template <typename T, typename TRAITS>
167 inline auto Synchronized<T, TRAITS>::rwget (const chrono::duration<double>& tryFor) -> WritableReference
168 requires (TRAITS::kSupportsTimedLocks)
169 {
170 [[maybe_unused]] unique_lock critSec{fMutex_, tryFor};
171 if (not critSec) [[unlikely]] {
172 Private_::ThrowTimeout_ ();
173 }
174 return WritableReference{this, move (critSec)};
175 }
176 template <typename T, typename TRAITS>
177 inline auto Synchronized<T, TRAITS>::operator->() const -> ReadableReference
178 {
179 return ReadableReference{this};
180 }
181 template <typename T, typename TRAITS>
183 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportSharedLocks)
184 {
185#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
186 Debug::TraceContextBumper ctx{L"Synchronized_Traits<MUTEX>::lock_shared", L"&m=%p", &m};
187#endif
188 fMutex_.lock_shared ();
189 NoteLockStateChanged_ (L"Locked Shared");
190 }
191 template <typename T, typename TRAITS>
193 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportSharedLocks)
194 {
195#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
196 Debug::TraceContextBumper ctx{L"Synchronized_Traits<MUTEX>::unlock_shared", L"&m=%p", &m};
197#endif
198 fMutex_.unlock_shared ();
199 NoteLockStateChanged_ (L"Unlocked Shared");
200 }
201 template <typename T, typename TRAITS>
202 inline void Synchronized<T, TRAITS>::lock () const
203 requires (TRAITS::kIsRecursiveReadMutex)
204 {
205#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
206 Debug::TraceContextBumper ctx{L"Synchronized<T, TRAITS>::lock", L"&fMutex_=%p", &fMutex_};
207#endif
208 fMutex_.lock ();
209 NoteLockStateChanged_ (L"Locked");
210 ++fWriteLockCount_;
211 }
212 template <typename T, typename TRAITS>
214 requires (TRAITS::kIsRecursiveReadMutex)
215 {
216#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
217 Debug::TraceContextBumper ctx{L"Synchronized<T, TRAITS>::try_lock", L"&fMutex_=%p", &fMutex_};
218#endif
219 bool result = fMutex_.try_lock ();
220 if (result) [[likely]] {
221 NoteLockStateChanged_ (L"Locked");
222 ++fWriteLockCount_;
223 }
224 return result;
225 }
226 template <typename T, typename TRAITS>
227 inline bool Synchronized<T, TRAITS>::try_lock_for (const chrono::duration<double>& tryFor) const
228 requires (TRAITS::kIsRecursiveReadMutex and TRAITS::kSupportsTimedLocks)
229 {
230#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
231 Debug::TraceContextBumper ctx{L"Synchronized<T, TRAITS>::try_lock_for", L"&fMutex_=%p", &fMutex_};
232#endif
233 bool result = fMutex_.try_lock_for (tryFor);
234 if (result) [[likely]] {
235 NoteLockStateChanged_ (L"Locked");
236 ++fWriteLockCount_;
237 }
238 return result;
239 }
240 template <typename T, typename TRAITS>
242 requires (TRAITS::kIsRecursiveReadMutex)
243 {
244#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
245 Debug::TraceContextBumper ctx{L"Synchronized<T, TRAITS>::unlock", L"&fMutex_=%p", &fMutex_};
246#endif
247 NoteLockStateChanged_ (L"Unlocked");
248 fMutex_.unlock ();
249 }
250 template <typename T, typename TRAITS>
251 inline bool Synchronized<T, TRAITS>::operator== (const Synchronized& rhs) const
252 requires (TRAITS::kIsRecursiveReadMutex and equality_comparable<T>)
253 {
254 return load () == rhs.load ();
255 }
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>)
259 {
260 return load () == rhs;
261 }
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>)
265 {
266 return load () <=> rhs.load ();
267 }
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>)
271 {
272 return load () <=> rhs;
273 }
274 template <typename T, typename TRAITS>
275 inline bool Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly ([[maybe_unused]] ReadableReference* lockBeingUpgraded,
276 const function<void (WritableReference&&)>& doWithWriteLock,
277 Time::DurationSeconds timeout)
278 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
279 {
280 return UpgradeLockNonAtomicallyQuietly (
281 lockBeingUpgraded,
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");
286#endif
287 return false;
288 }
289 else {
290 doWithWriteLock (std::move (wRef));
291 return true;
292 }
293 },
294 timeout);
296 template <typename T, typename TRAITS>
297 bool Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly ([[maybe_unused]] ReadableReference* lockBeingUpgraded,
298 const function<bool (WritableReference&&, bool interveningWriteLock)>& doWithWriteLock,
299 Time::DurationSeconds timeout)
300 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
301 {
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_,
305 Characters::ToString (timeout).c_str ()};
306#endif
307 RequireNotNull (lockBeingUpgraded);
308 Require (lockBeingUpgraded->fSharedLock_.mutex () == &fMutex_);
309 Require (lockBeingUpgraded->fSharedLock_.owns_lock ());
310 auto writeLockCountBeforeReleasingReadLock = fWriteLockCount_;
311 fMutex_.unlock_shared ();
312 [[maybe_unused]] auto&& cleanup = Execution::Finally ([this] () noexcept {
313 fMutex_.lock_shared (); // this API requires (regardless of timeout) that we re-lock (shared)
314 NoteLockStateChanged_ (L"in Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly finally relocked shared");
315 });
316 typename TRAITS::WriteLockType upgradeLock{fMutex_, std::defer_lock}; // NOTE ONLY held til the end of this and doWithWriteLock
317 if (timeout >= Time::DurationSeconds::max ()) {
318 upgradeLock.lock (); // if wait 'infinite' use no-time-arg lock call
319 }
320 else {
321 if (not upgradeLock.try_lock_for (timeout)) {
322 return false;
323 }
324 }
325 NoteLockStateChanged_ (L"in Synchronized<T, TRAITS>::UpgradeLockNonAtomicallyQuietly acquired Lock"); // no need to message on unlock cuz lock transfered to WritableReference that messages on unlock
326 WritableReference wr = WritableReference{this, std::move (upgradeLock)};
327 bool interveningWriteLock = fWriteLockCount_ > 1 + writeLockCountBeforeReleasingReadLock;
328 // pass 'interveningWriteLock' flag to callback so IT can decide how to handle - ignore or throw
329 return doWithWriteLock (std::move (wr), interveningWriteLock);
330 }
331 template <typename T, typename TRAITS>
332 inline void Synchronized<T, TRAITS>::UpgradeLockNonAtomically ([[maybe_unused]] ReadableReference* lockBeingUpgraded,
333 const function<void (WritableReference&&)>& doWithWriteLock,
335 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
336 {
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_,
339 Characters::ToString (timeout).c_str ()};
340#endif
341 if (not UpgradeLockNonAtomicallyQuietly (lockBeingUpgraded, doWithWriteLock, timeout)) [[unlikely]] {
342 Execution::Private_::ThrowTimeout_ (); // @todo a bit of a defect, could be returned false not due to timeout, but do to doWithWriteLock returning false...
343 }
344 }
345 template <typename T, typename TRAITS>
346 void Synchronized<T, TRAITS>::UpgradeLockNonAtomically ([[maybe_unused]] ReadableReference* lockBeingUpgraded,
347 const function<bool (WritableReference&&, bool interveningWriteLock)>& doWithWriteLock,
348 Time::DurationSeconds timeout)
349 requires (TRAITS::kSupportSharedLocks and TRAITS::kSupportsTimedLocks)
350 {
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_,
353 Characters::ToString (timeout).c_str ()};
354#endif
355 if (not UpgradeLockNonAtomicallyQuietly (lockBeingUpgraded, doWithWriteLock, timeout)) [[unlikely]] {
356 Execution::Private_::ThrowTimeout_ (); // @todo a bit of a defect, could be returned false not due to timeout, but do to doWithWriteLock returning false...
357 }
358 }
359 template <typename T, typename TRAITS>
360 inline void Synchronized<T, TRAITS>::NoteLockStateChanged_ ([[maybe_unused]] const wchar_t* m) const noexcept
361 {
362 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
363 if (this->fDbgTraceLocksName) {
364#if qStroika_Foundation_Debug_DefaultTracingOn
365 Private_::DbgTraceHelper_ (m, this->fDbgTraceLocksName);
366#endif
367 }
368 }
369 }
370
371 /*
372 ********************************************************************************
373 ***************** Synchronized<T, TRAITS>::ReadableReference *******************
374 ********************************************************************************
375 */
376 template <typename T, typename TRAITS>
378 : fT{(RequireExpression (s != nullptr), &s->fProtectedValue_)}
379 {
380 RequireNotNull (s);
381#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
382 DbgTrace (L"ReadableReference::CTOR -- locks (_eExternallyLocked)");
383#endif
384 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
385 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
387 }
388 template <typename T, typename TRAITS>
389 inline Synchronized<T, TRAITS>::ReadableReference::ReadableReference (const Synchronized* s, ReadLockType_&& readLock)
390 : fT{(RequireExpression (s != nullptr), &s->fProtectedValue_)}
391 , fSharedLock_{move (readLock)}
392 {
393 RequireNotNull (s);
394#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
395 DbgTrace (L"ReadableReference::CTOR -- locks (readLock)");
396#endif
397 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
398 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
399 }
400 }
401 template <typename T, typename TRAITS>
403 : fT{(RequireExpression (s != nullptr), &s->fProtectedValue_)}
404 , fSharedLock_{(RequireExpression (s != nullptr), s->fMutex_)}
405 {
406 RequireNotNull (fT);
407#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
408 DbgTrace (L"ReadableReference::CTOR -- locks (fSharedLock_ with mutex_=%p)", &s->fMutex_);
409#endif
410 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
411 this->fDbgTraceLocksName = s->fDbgTraceLocksName;
412 }
413 _NoteLockStateChanged (L"ReadableReference Locked");
415 template <typename T, typename TRAITS>
417 : fT{src.fT} // its a pointer so move same as copy
418 , fSharedLock_{std::move (src.fSharedLock_)}
419 {
420 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
421 this->fDbgTraceLocksName = move (src.fDbgTraceLocksName);
422 }
423 _NoteLockStateChanged (L"ReadableReference move-Locked");
424#if qStroika_Foundation_Debug_AssertionsChecked
425 src.fT = nullptr;
426#endif
427 //src.fSharedLock_ = nullptr;
428 }
429 template <typename T, typename TRAITS>
431 {
432#if Stroika_Foundation_Execution_Synchronized_USE_NOISY_TRACE_IN_THIS_MODULE_
433 DbgTrace (L"ReadableReference::DTOR -- locks (fSharedLock_ mutex_=%p)", fSharedLock_);
434#endif
435 if (fSharedLock_.owns_lock ()) {
436 _NoteLockStateChanged (L"ReadableReference Unlocked");
437 }
438 }
439 template <typename T, typename TRAITS>
441 {
442 EnsureNotNull (fT);
443 return fT;
444 }
445 template <typename T, typename TRAITS>
448 EnsureNotNull (fT);
449 return *fT;
450 }
451 template <typename T, typename TRAITS>
453 {
454 EnsureNotNull (fT);
455 return *fT;
456 }
457 template <typename T, typename TRAITS>
459 {
460 EnsureNotNull (fT);
461 return *fT;
462 }
463 template <typename T, typename TRAITS>
464 inline void Synchronized<T, TRAITS>::ReadableReference::_NoteLockStateChanged ([[maybe_unused]] const wchar_t* m) const
465 {
466 if constexpr (TRAITS::kDbgTraceLockUnlockIfNameSet) {
467 if (this->fDbgTraceLocksName) {
468#if qStroika_Foundation_Debug_DefaultTracingOn
469 Private_::DbgTraceHelper_ (m, this->fDbgTraceLocksName);
470#endif
471 }
472 }
473 }
474
475 /*
476 ********************************************************************************
477 *************** Synchronized<T, TRAITS>::WritableReference *********************
478 ********************************************************************************
479 */
480 template <typename T, typename TRAITS>
481 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s)
482 : ReadableReference{s, ReadableReference::_ExternallyLocked::_eExternallyLocked}
483 , fWriteLock_{s->fMutex_}
484 {
485 RequireNotNull (s);
486 this->_NoteLockStateChanged (L"WritableReference Locked");
487 ++s->fWriteLockCount_;
488 }
489 template <typename T, typename TRAITS>
490 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s, WriteLockType_&& writeLock)
491 : ReadableReference{s, ReadableReference::_ExternallyLocked::_eExternallyLocked}
492 , fWriteLock_{std::move (writeLock)}
493 {
494 RequireNotNull (s);
495 this->_NoteLockStateChanged (L"WritableReference move-Locked");
496 ++s->fWriteLockCount_; // update lock count cuz though not a new lock, new to WritableReference
497 // and just used outside construct of WritableReference to control how lock acquired
498 }
499 template <typename T, typename TRAITS>
500 inline Synchronized<T, TRAITS>::WritableReference::WritableReference (Synchronized* s, Time::DurationSeconds timeout)
501 : ReadableReference{s, ReadableReference::_ExternallyLocked::_eExternallyLocked}
502 , fWriteLock_{s->fMutex_, timeout}
503 {
504 RequireNotNull (s);
505 if (not fWriteLock_.owns_lock ()) [[unlikely]] {
506 Execution::Private_::ThrowTimeout_ ();
507 }
508 this->_NoteLockStateChanged (L"WritableReference Locked");
509 ++s->fWriteLockCount_;
510 }
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_)}
515 {
516 // no change to writelock count cuz not a new lock - just moved
517 this->_NoteLockStateChanged (L"WritableReference move-Locked");
518 }
519 template <typename T, typename TRAITS>
520 inline auto Synchronized<T, TRAITS>::WritableReference::operator= (T rhs) -> const WritableReference&
521 {
522 RequireNotNull (this->fT);
523 // const_cast Safe because the only way to construct one of these is from a non-const pointer, or another WritableReference
524 *const_cast<T*> (this->fT) = rhs;
525 return *this;
526 }
527 template <typename T, typename TRAITS>
528 inline T* Synchronized<T, TRAITS>::WritableReference::operator->()
529 {
530 // const_cast Safe because the only way to construct one of these is from a non-const pointer, or another WritableReference
531 EnsureNotNull (this->fT);
532 return const_cast<T*> (this->fT);
533 }
534 template <typename T, typename TRAITS>
536 {
537 return ReadableReference::operator->();
538 }
539 template <typename T, typename TRAITS>
540 inline T& Synchronized<T, TRAITS>::WritableReference::rwref ()
541 {
542 // const_cast Safe because the only way to construct one of these is from a non-const pointer, or another WritableReference
543 EnsureNotNull (this->fT);
544 return *const_cast<T*> (this->fT);
545 }
546 template <typename T, typename TRAITS>
547 inline void Synchronized<T, TRAITS>::WritableReference::store (const T& v)
548 {
549 rwref () = v;
550 }
551 template <typename T, typename TRAITS>
552 inline void Synchronized<T, TRAITS>::WritableReference::store (T&& v)
553 {
554 rwref () = std::move (v);
555 }
556
557 /*
558 ********************************************************************************
559 ********************************** operator^ ***********************************
560 ********************************************************************************
561 */
562 template <typename T, typename TRAITS>
563 inline auto operator^ (const Synchronized<T, TRAITS>& lhs, T rhs) -> decltype (T{} ^ T{})
564 {
565 return lhs.load () ^ rhs;
566 }
567 template <typename T, typename TRAITS>
568 inline auto operator^ (T lhs, const Synchronized<T, TRAITS>& rhs) -> decltype (T{} ^ T{})
569 {
570 return lhs ^ rhs.load ();
571 }
572 template <typename T, typename TRAITS>
573 inline auto operator^ (const Synchronized<T, TRAITS>& lhs, const Synchronized<T, TRAITS>& rhs) -> decltype (T{} ^ T{})
574 {
575 // preload to avoid possible deadlock
576 auto l = lhs.load ();
577 auto r = rhs.load ();
578 return l ^ r;
579 }
580
581 /*
582 ********************************************************************************
583 ********************************** operator* ***********************************
584 ********************************************************************************
585 */
586 template <typename T, typename TRAITS>
587 inline auto operator* (const Synchronized<T, TRAITS>& lhs, T rhs) -> decltype (T{} * T{})
588 {
589 return lhs.load () * rhs;
590 }
591 template <typename T, typename TRAITS>
592 inline auto operator* (T lhs, const Synchronized<T, TRAITS>& rhs) -> decltype (T{} * T{})
593 {
594 return lhs * rhs.load ();
595 }
596 template <typename T, typename TRAITS>
597 inline auto operator* (const Synchronized<T, TRAITS>& lhs, const Synchronized<T, TRAITS>& rhs) -> decltype (T{} * T{})
598 {
599 // preload to avoid possible deadlock
600 auto l = lhs.load ();
601 auto r = rhs.load ();
602 return l * r;
603 }
604
605 /*
606 ********************************************************************************
607 ********************************** operator+ ***********************************
608 ********************************************************************************
609 */
610 template <typename T, typename TRAITS>
611 inline auto operator+ (const Synchronized<T, TRAITS>& lhs, T rhs) -> decltype (T{} + T{})
612 {
613 return lhs.load () + rhs;
614 }
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 ();
619 }
620 template <typename T, typename TRAITS>
621 inline auto operator+ (const Synchronized<T, TRAITS>& lhs, const Synchronized<T, TRAITS>& rhs) -> decltype (T{} + T{})
622 {
623 // preload to avoid possible deadlock
624 auto l = lhs.load ();
625 auto r = rhs.load ();
626 return l + r;
627 }
628
629 /*
630 ********************************************************************************
631 ********************************** operator-= **********************************
632 ********************************************************************************
633 */
634 template <typename T, typename TRAITS, typename RHSTYPE>
635 inline auto operator-= (Synchronized<T, TRAITS>& lhs, RHSTYPE rhs) -> decltype (lhs.rwget ()->operator-= (rhs))
636 {
637 return lhs.rwget ()->operator-= (rhs);
638 }
640 /*
641 ********************************************************************************
642 ********************************** operator+= **********************************
643 ********************************************************************************
644 */
645 template <typename T, typename TRAITS, typename RHSTYPE>
646 inline auto operator+= (Synchronized<T, TRAITS>& lhs, RHSTYPE rhs) -> decltype (lhs.rwget ()->operator+= (rhs))
647 {
648 return lhs.rwget ()->operator+= (rhs);
649 }
650
651 /*
652 ********************************************************************************
653 ********************************** operator- ***********************************
654 ********************************************************************************
655 */
656 template <typename T, typename TRAITS>
657 inline auto operator- (const Synchronized<T, TRAITS>& lhs, T rhs) -> decltype (T{} - T{})
659 return lhs.load () + rhs;
660 }
661 template <typename T, typename TRAITS>
662 inline auto operator- (T lhs, const Synchronized<T, TRAITS>& rhs) -> decltype (T{} - T{})
663 {
664 return lhs - rhs.load ();
665 }
666 template <typename T, typename TRAITS>
667 inline auto operator- (const Synchronized<T, TRAITS>& lhs, const Synchronized<T, TRAITS>& rhs) -> decltype (T{} - T{})
668 {
669 // preload to avoid possible deadlock
670 auto l = lhs.load ();
671 auto r = rhs.load ();
672 return l - r;
673 }
674
675}
#define EnsureNotNull(p)
Definition Assertions.h:343
#define RequireNotNull(p)
Definition Assertions.h:350
#define RequireExpression(c)
Definition Assertions.h:270
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
_ExternallyLocked
#define DbgTrace
Definition Trace.h:327
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Definition String.inl:1053
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 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 ReadableReference cget() const
get a read-only smart pointer to the underlying Synchronized<> object, holding the readlock the whole...
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Definition ToString.inl:424
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31
STL namespace.