Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
AssertExternallySynchronizedChecker.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include <algorithm> // for std::count/std::distance
5
6#include "Sanitizer.h"
7
8namespace Stroika::Foundation::Debug {
9
10 /*
11 ********************************************************************************
12 *********** Debug::AssertExternallySynchronizedChecker::SharedContext ************
13 ********************************************************************************
14 */
15#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
16 inline AssertExternallySynchronizedChecker::SharedContext ::~SharedContext ()
17 {
18 Assert (fFullLocks_ == 0);
19 Assert (fSharedLocks_.fOverflowThreads_.empty () and fSharedLocks_.fInitialThreadsSize_ == 0);
20 }
21 inline bool AssertExternallySynchronizedChecker ::SharedContext ::GetSharedLockEmpty_ () const
22 {
23 lock_guard<mutex> sharedLockProtect{GetSharedLockMutexThreads_ ()};
24 return fSharedLocks_.fInitialThreadsSize_ == 0 and fSharedLocks_.fOverflowThreads_.empty ();
25 }
26 inline pair<size_t, size_t> AssertExternallySynchronizedChecker ::SharedContext ::CountSharedLockThreads_ () const
27 {
28 auto tid = this_thread::get_id ();
29 lock_guard<mutex> sharedLockProtect{GetSharedLockMutexThreads_ ()};
30 size_t thisThreadCnt = std::count (fSharedLocks_.fInitialThreads_.begin (),
31 fSharedLocks_.fInitialThreads_.begin () + fSharedLocks_.fInitialThreadsSize_, tid) and
32 std::count (fSharedLocks_.fOverflowThreads_.begin (), fSharedLocks_.fOverflowThreads_.end (), tid);
33 size_t otherThreadCnt = fSharedLocks_.fInitialThreadsSize_ +
34 std::distance (fSharedLocks_.fOverflowThreads_.begin (), fSharedLocks_.fOverflowThreads_.end ());
35 otherThreadCnt -= thisThreadCnt;
36 return make_pair (thisThreadCnt, otherThreadCnt);
37 }
38 inline size_t AssertExternallySynchronizedChecker ::SharedContext ::GetSharedLockThreadsCount_ () const
39 {
40 lock_guard<mutex> sharedLockProtect{GetSharedLockMutexThreads_ ()};
41 return fSharedLocks_.fInitialThreadsSize_ + std::distance (fSharedLocks_.fOverflowThreads_.begin (), fSharedLocks_.fOverflowThreads_.end ());
42 }
43 inline size_t AssertExternallySynchronizedChecker ::SharedContext ::CountOfIInSharedLockThreads_ (thread::id i) const
44 {
45 lock_guard<mutex> sharedLockProtect{GetSharedLockMutexThreads_ ()};
46 return std::count (fSharedLocks_.fInitialThreads_.begin (), fSharedLocks_.fInitialThreads_.begin () + fSharedLocks_.fInitialThreadsSize_, i) +
47 std::count (fSharedLocks_.fOverflowThreads_.begin (), fSharedLocks_.fOverflowThreads_.end (), i);
48 }
49 inline void AssertExternallySynchronizedChecker ::SharedContext ::AddSharedLock_ (thread::id i)
50 {
51 lock_guard<mutex> sharedLockProtect{GetSharedLockMutexThreads_ ()};
52 if (fSharedLocks_.fInitialThreadsSize_ < kInlineSharedLockBufSize_) {
53 fSharedLocks_.fInitialThreads_[fSharedLocks_.fInitialThreadsSize_++] = i;
54 }
55 else {
56 fSharedLocks_.fOverflowThreads_.push_front (i);
57 }
58 }
59 inline void AssertExternallySynchronizedChecker ::SharedContext ::RemoveSharedLock_ (thread::id i)
60 {
61 lock_guard<mutex> sharedLockProtect{GetSharedLockMutexThreads_ ()};
62 if constexpr (kInlineSharedLockBufSize_ != 0) {
63 auto re = fSharedLocks_.fInitialThreads_.begin () + fSharedLocks_.fInitialThreadsSize_;
64 auto ri = find (fSharedLocks_.fInitialThreads_.begin (), re, i);
65 if (ri != re) {
66 if (ri + 1 != re) {
67 copy (ri + 1, re, ri); // if test not useful if optimized, but this code mainly used unoptimized and appears to help there
68 }
69 --fSharedLocks_.fInitialThreadsSize_;
70 return;
71 }
72 }
73 auto re = fSharedLocks_.fOverflowThreads_.end ();
74 for (auto beforeI = fSharedLocks_.fOverflowThreads_.before_begin ();; ++beforeI) {
75 Assert (beforeI != re);
76 auto n = beforeI;
77 n++;
78 Assert (n != re);
79 if (*n == i) {
80 fSharedLocks_.fOverflowThreads_.erase_after (beforeI);
81 return;
82 }
83 }
85 }
86#endif
87
88 /*
89 ********************************************************************************
90 **************** Debug::AssertExternallySynchronizedChecker **********************
91 ********************************************************************************
92 */
93#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
94 inline AssertExternallySynchronizedChecker::AssertExternallySynchronizedChecker (const shared_ptr<SharedContext>& sharedContext) noexcept
95 // https://github.com/SophistSolutions/Stroika/issues/634 (STK-500)
96 // NOTE - this will generate a throw and std::unexpected violation if there is no memory and multiset CTOR
97 // throws. There is no good answer in this case. We declare the constructors noexcept so the footprint of
98 // AssertExternallySynchronizedChecker is as light as possible and the same (API/constraints) between debug and release
99 // builds. And if we run out of memory here, there isn't much we can do to continue -- LGP 2018-10-02
100 : fSharedContext_{sharedContext ? sharedContext : make_shared<SharedContext> ()}
101 {
102 }
103 inline AssertExternallySynchronizedChecker::AssertExternallySynchronizedChecker (const shared_ptr<SharedContext>& sharedContext,
104 const AssertExternallySynchronizedChecker& src) noexcept
105 : AssertExternallySynchronizedChecker{sharedContext}
106 {
107 ReadContext readLockSrc{src}; // to copy, the src can have shared_locks, but no (write) locks
108 }
109 inline AssertExternallySynchronizedChecker::AssertExternallySynchronizedChecker (const AssertExternallySynchronizedChecker& src) noexcept
110 : AssertExternallySynchronizedChecker{}
111 {
112 ReadContext readLockSrc{src}; // to copy, the src can have shared_locks, but no (write) locks
113 }
114 inline AssertExternallySynchronizedChecker::AssertExternallySynchronizedChecker (const shared_ptr<SharedContext>& sharedContext,
115 [[maybe_unused]] AssertExternallySynchronizedChecker&& src) noexcept
116 : AssertExternallySynchronizedChecker{sharedContext}
117 {
118 WriteContext declareWriteContext4Src{src}; // move we must be able to modify source
119 }
120 inline AssertExternallySynchronizedChecker::AssertExternallySynchronizedChecker ([[maybe_unused]] AssertExternallySynchronizedChecker&& src) noexcept
121 : AssertExternallySynchronizedChecker{}
122 {
123 WriteContext writeLockRHS{src}; // move we must be able to modify source
124 }
125#endif
126 inline AssertExternallySynchronizedChecker&
127 AssertExternallySynchronizedChecker::operator= ([[maybe_unused]] const AssertExternallySynchronizedChecker& rhs) noexcept
128 {
129 ReadContext readLockRHS{rhs}; // we must be able to read RHS
130 WriteContext declareWriteContext4This{*this}; // we must be able modify this
131 return *this;
132 }
134 {
135 WriteContext writeLockRHS{rhs}; // move we must be able to modify rhs to move it
136 WriteContext writeLockThis{*this}; // we must be able modify this
137 return *this;
138 }
139#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
140 inline auto AssertExternallySynchronizedChecker::GetSharedContext () const -> shared_ptr<SharedContext>
141 {
142 return fSharedContext_;
143 }
144 inline void AssertExternallySynchronizedChecker::SetAssertExternallySynchronizedCheckerContext (const shared_ptr<SharedContext>& sharedContext)
145 {
146 Require (sharedContext != nullptr);
147 fSharedContext_ = sharedContext;
148 }
149#endif
151 {
152#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
153 lock_ ();
154#endif
155 }
157 {
158#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
159 return try_lock_ ();
160#else
161 return true;
162#endif
163 }
165 {
166#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
167 unlock_ ();
168#endif
169 }
171 {
172#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
173 lock_shared_ ();
174#endif
175 }
177 {
178#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
179 unlock_shared_ ();
180#endif
181 }
182
183}
#define RequireNotReached()
Definition Assertions.h:386
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
unique_lock< AssertExternallySynchronizedChecker > WriteContext
Instantiate AssertExternallySynchronizedChecker::WriteContext to designate an area of code where prot...
nonvirtual AssertExternallySynchronizedChecker & operator=(AssertExternallySynchronizedChecker &&rhs) noexcept
nonvirtual bool try_lock() noexcept
Like lock() - if it would succeed, same then, but if would fail instead of assert out,...