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