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