ALib C++ Framework
by
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
dbgasserters.inl
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_threads of the \aliblong.
4///
5/// \emoji :copyright: 2013-2025 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8#if !ALIB_SINGLE_THREADED && ALIB_DEBUG
9
10ALIB_EXPORT namespace alib::threads {
11
12//==================================================================================================
13/// This type is used for debugging and asserting \alib lock (mutex) types.
14/// With debug compilations the non-shared lock types hold one member of this struct, which
15/// aggregates all debug information.
16///
17/// Todo: Add a transaction number. This has to be displayed with every output.
18//==================================================================================================
20{
21 /// The name of this Lock.
22 /// Set to <em> "<unnamed>"</em> by default.
23 /// Used for debug-output.
24 /// For automatic pruning of changes to this name, macro #"ALIB_DBG" should be used.
25 const char* Name ="<unnamed>";
26 CallerInfo AcqCI ; ///< Source location of the most recent acquirement.
27 CallerInfo RelCI ; ///< Source location of the most recent release.
28
29 std::atomic<int> CntAcquirements{0}; ///< The number of shared acquirements.
30
31 /// The format string used to write exceptions to the console.
32 /// This string can be changed if the source information is not "clickable" in a user's
33 /// development environment.<br>
34 ///
35 /// The default string is optimized for
36 /// \https{JetBrains CLion,www.jetbrains.com/clion} and is defined as:
37 /** \verbatim
38Multi-Threading {} in Lock \"{}\"
39 Message: {}
40 In (Member-)Function: {}
41 Is Owned: {} ({})
42
43 Called By: {}::{}
44 At: {}:{}
45 Thread: {}
46
47 Latest Acquisition By: {}::{}
48 At: {}:{}
49 Thread: {}
50 Latest Release By: {}:{}
51 At: {}:{}
52 Thread: {}
53 \endverbatim
54 <p>
55 The placeholder fields that this format string refers to are set as follows:
56 - \c 0: String "Assertion" or "Warning"
57 - \c 1: Debug-name of the lock.
58 - \c 2: Headline.
59 - \c 3: Function name.
60 - \c 4: Is acquired (true/false).
61 - \c 5: number of acquirements.
62 - \c 6-10: \b %CallerInfo of caller.
63 - \c 11-15: \b %CallerInfo of the latest acquisition.
64 - \c 16-20: \b %CallerInfo of the latest release.
65*/
67 static const char* ASSERTION_FORMAT;
68
69
70 /// This is a threshold that causes non-timed Acquire() methods to raise a \alib_warning in
71 /// debug-builds in case a thread is blocked longer than the given duration.
72 ///
73 /// To disable warnings in cases that high block times are suitable, set this value to \c 0.
74 /// The default value is two seconds.
75 Ticks::Duration WaitTimeLimit = Ticks::Duration::FromAbsoluteSeconds(2);
76
77 /// Limit of recursions. If the limit is reached or a multiple of it, an
78 /// \alib_warning is raised.
79 /// Defaults is \c 10. To disable, set to \c 0.<p>
80 /// Available only in debug versions of \alib.
82
83 /// Destructor.
84 virtual ~DbgLockAsserter() {
85 if( CntAcquirements.load() > 0 )
86 DoAssert( 0, ALIB_CALLER, ALIB_CALLER, "Destructing acquired lock" );
87 }
88
89 /// Returns a pointer the owning thread.<p>
90 /// Available only in debug-builds.
91 /// @return Pointer to the owning thread, or \c nullptr if not owned.
93 Thread* GetOwner() const;
94
95 /// Returns \c true if the current owner is the current thread.<p>
96 /// Available only in debug-builds.
97 /// @return \c true, if the lock is owned by this thread, \c false if it is owned by
98 /// another thread or not owned.
100 { return CntAcquirements.load() > 0 && AcqCI.ThreadID == std::this_thread::get_id(); }
101
102 /// Returns \c true if this is a non-recursive lock or a recursive instance which is acquired
103 /// exactly once.<br>
104 /// Available only in debug-builds.
105 ///
106 /// \note
107 /// This method is not (and cannot) be synchronized. Consequently, a reliable result
108 /// is only guaranteed in case #".IsOwnedByCurrentThread" returns \c true before this method
109 /// is invoked.
110 ///
111 /// @return \c true if the next release will free this lock, \c false otherwise.
112 bool WillRelease() const noexcept { return CntAcquirements.load()== 1; }
113
114 /// Collects assertion info and #"alib_mod_assert;raises a warning or error".
115 /// @see
116 /// Field #".ASSERTION_FORMAT" which allows changing the output format to achieve 'clickable'
117 /// assertion messages.
118 /// @param type 0= assertion, 1= warning.
119 /// @param assertCI Location where the assertion is placed.
120 /// @param ci Location of the call to the method that asserted.
121 /// @param headline The message.
123 virtual
124 void DoAssert (int type, const CallerInfo& assertCI, const CallerInfo& ci,
125 const char* headline );
126
127 /// Asserts that #"CntAcquirements" is not \c 0
128 /// @param assertCI Location where the assertion is placed.
129 /// @param ci Location of the call to the method that asserted.
130 void AssertOwned (const CallerInfo& assertCI, const CallerInfo& ci )
131 {
132 if( CntAcquirements.load() == 0 )
133 DoAssert( 0, assertCI, ci, "Not acquired" );
134 }
135
136 /// Sets the given caller as the current owner. Asserts that #"CntAcquirements" is \c 0
137 /// when called.
138 /// @param assertCI Location where ownership was implemented (usually the lock).
139 /// @param requestCI Location where ownership was requested (the caller of \p{assertCI})
140 void SetOwner (const CallerInfo& assertCI, const CallerInfo& requestCI ) {
141 if( CntAcquirements.load() > 0 )
142 DoAssert( 0, assertCI, requestCI, "Already (still) owned." );
143 AcqCI= requestCI;
144 CntAcquirements.fetch_add(1);
145 }
146
147 /// Sets the given caller as the current owner. Asserts that either #"CntAcquirements" is \c 0
148 /// or the current owner is the same as in parameter \p{requestCI}.
149 /// @param assertCI Location where ownership was implemented (usually the lock).
150 /// @param requestCI Location where ownership was requested (the caller of \p{assertCI})
152 void SetRecursiveOwner (const CallerInfo& assertCI, const CallerInfo& requestCI );
153
154 /// Asserts that this lock is owned by the thread in \p{ci}.
155 /// @param assertCI Location where the release is implemented.
156 /// @param requestCI Location where the release was requested (the caller of \p{assertCI})
157 void Release (const CallerInfo& assertCI, const CallerInfo& requestCI) {
158 if( CntAcquirements.load() == 0 || requestCI.ThreadID != AcqCI.ThreadID )
159 DoAssert( 0, assertCI, requestCI, "Release without having ownership");
160 RelCI = requestCI;
161 CntAcquirements.fetch_sub(1);
162 }
163
164 /// Asserts that this lock is not owned by the thread in \p{ci}.
165 /// @param assertCI Location where the assertion is placed.
166 /// @param ci Location of the call to the method that asserted.
167 /// @param headline The message.
168 void AssertNotOwning(const CallerInfo& assertCI, const CallerInfo& ci, const char* headline )
169 {
170 if( CntAcquirements.load() > 0 && ci.ThreadID == AcqCI.ThreadID )
171 DoAssert( 0, assertCI, ci, headline);
172 }
173}; // struct DbgLockAsserter
174
175//==================================================================================================
176/// This type is used for debugging and asserting \alib lock (mutex) types.
177/// With debug compilations the shared lock types hold one member of this struct, which
178/// aggregates all debug information.
179//==================================================================================================
181{
182 CallerInfo SAcqCI; ///< The most recent shared acquirement's caller.
183 CallerInfo SRelCI; ///< The most recent shared release caller.
184 std::atomic<int> CntSharedAcquirements{0}; ///< The number of shared acquirements.
185
186 /// The format string used to write exceptions to the console.
187 /// This string can be changed if the source information is not "clickable" in a user's
188 /// development environment.<br>
189 ///
190 /// The default string is optimized for
191 /// \https{JetBrains CLion,www.jetbrains.com/clion} and is defined as:
192 /** \verbatim
193Multi-Threading {} in Shared-Lock \"{}\"
194 Message: {}
195 In (Member-)Function: {}
196 Is Owned: {} ({})
197 Is Shared Owned: {} ({})
198
199 Called By: {}::{}
200 At: {}:{}
201 Thread: {}
202
203 Latest Acquisition By: {}::{}
204 At: {}:{}
205 Thread: {}
206 Latest Release By: {}::{}
207 At: {}:{}
208 Thread: {}
209
210 Latest Shared Acquisition By: {}::{}
211 At: {}:{}
212 Thread: {}
213 Latest SharedRelease By: {}::{}
214 At: {}:{}
215 Thread: {}
216 \endverbatim
217 <p>
218 The placeholder fields that this format string refers to are set as follows:
219
220 - \c 0: String "Assertion" or "Warning"
221 - \c 1: Debug-name of the lock.
222 - \c 2: Headline.
223 - \c 3: Function name.
224 - \c 4: Is acquired (true/false).
225 - \c 5: number of acquirements.
226 - \c 6: Is shared acquired (true/false).
227 - \c 7: number of shared-acquirements.
228 - \c 8-12: \b %CallerInfo of caller.
229 - \c 13-17: \b %CallerInfo of the latest acquisition.
230 - \c 18-22: \b %CallerInfo of the latest release.
231 - \c 23-27: \b %CallerInfo of the latest shared-acquisition.
232 - \c 32-36: \b %CallerInfo of the latest shared-release.
233 */
235 static const char* ASSERTION_FORMAT_SHARED;
236
237
238 /// Destructor.
239 virtual ~DbgSharedLockAsserter() override {}
240
241 /// Assembles assertion info and #"alib_mod_assert;raises a warning or an error".
242 /// @see
243 /// Inherited field #"DbgLockAsserter::ASSERTION_FORMAT" which allows changing the output
244 /// format to achieve 'clickable' assertion messages.
245 /// @param type 0= assertion, 1= warning.
246 /// @param assertCI Location where the assertion is placed.
247 /// @param ci Location of the call to the method that asserted.
248 /// @param headline The message.
250 void DoAssert (int type, const CallerInfo& assertCI, const CallerInfo& ci,
251 const char* headline ) override;
252
253 /// Returns \c true if currently a reader is registered. This method is used to
254 /// create assertions. of course, to detect assertions, it would be more efficient to check
255 /// if shared ownership is with the current thread, but a check on this would cost
256 /// a lot of overhead to realize. This way, at least assertions can be raised when no other
257 /// thread acquired this lock in shared mode.<p>
258 /// Available only in debug-builds.
259 /// @return \c true, if the lock is owned by this thread, \c false if it is owned by
260 /// another thread or not owned.
261 bool IsSharedOwnedByAnyThread() const { return CntSharedAcquirements.load() > 0; }
262
263 /// Sets the given caller as the current owner. Asserts that #"CntAcquirements" is \c 0
264 /// when called.
265 /// @param assertCI Location where ownership was implemented (usually the lock).
266 /// @param requestCI Location where ownership was requested (the caller of \p{assertCI})
267 void SetOwner (const CallerInfo& assertCI, const CallerInfo& requestCI ) {
268 if( CntAcquirements.load() > 0 )
269 DoAssert( 0, assertCI, requestCI, "Already (still) owned." );
270 if( CntSharedAcquirements.load() > 0 )
271 DoAssert( 0, assertCI, requestCI, "Already (still) shared-owned." );
272 AcqCI= requestCI;
273 CntAcquirements.fetch_add(1);
274 }
275
276 /// Sets the given caller as a current shared-owner. Asserts that #"CntAcquirements" is \c 0
277 /// when called.
278 /// @param assertCI Location where ownership was implemented (usually the lock).
279 /// @param requestCI Location where ownership was requested (the caller of \p{assertCI})
280 /// @param warnLimit If this number of shared acquisitions is exceeded, a warning is
281 /// given.
282 void SetSharedOwner (const CallerInfo& assertCI, const CallerInfo& requestCI,
283 int warnLimit ) {
284 if( CntAcquirements.load() > 0 )
285 DoAssert( 0, assertCI, requestCI, "Already (still) owned." );
286
287 if ( CntSharedAcquirements.fetch_add(1) >= warnLimit )
288 DoAssert( 1, ALIB_CALLER, requestCI,
289 "Too many parallel shared acquisitions detected. "
290 "A reason might be that shared acquirers do not call ReleaseShared" );
291
292 SAcqCI= requestCI;
293 }
294
295 /// Asserts that this lock is shared-owned by the thread in \p{ci}.
296 /// @param assertCI Location where the release is implemented.
297 /// @param requestCI Location where the release was requested (the caller of \p{assertCI})
298 void ReleaseShared (const CallerInfo& assertCI, const CallerInfo& requestCI) {
299 auto prevCounter= CntSharedAcquirements.fetch_sub(1);
300 if ( prevCounter <= 0 )
301 DoAssert( 0, assertCI, requestCI,
302 "Too many invocations of ReleaseShared (from any thread) without prior acquisition" );
303
304 SRelCI= requestCI;
305 }
306
307}; // struct DbgSharedLockAsserter
308
309
310//==================================================================================================
311/// This type is used for debugging and asserting types #"TCondition".
312/// With debug compilations that class holds one member of this struct, which aggregates all
313/// debug information.
314//==================================================================================================
316{
317 const character* Name; ///< The name of this instance.
318 Thread* Owner{nullptr}; ///< Tracks the current owner.
319 CallerInfo AcqCI ; ///< Source location of the most recent acquirement.
320 CallerInfo RelCI ; ///< Source location of the most recent release.
321 CallerInfo WaitCI; ///< The most recent call to \b WaitForNotification.
322 CallerInfo NotifyCI; ///< The most recent call to \b ReleaseAndNotify or
323 std::atomic<int> CntWaiters{0}; ///< The number of currently waiting threads.
324 std::thread::id AssertExclusiveWaiter; ///< If set from outside, methods \b WaitForNotification
325 ///< will raise an assertion in the case they are called
326 ///< from a different thread.
327 /// The format string used to write exceptions to the console.
328 /// This string can be changed if the source information is not "clickable" in a user's
329 /// development environment.<br>
330 ///
331 /// The default string is optimized for
332 /// \https{JetBrains CLion,www.jetbrains.com/clion} and is defined as:
333 /** \verbatim
334Assertion failed in method TCondition::{}
335 Message: {}
336 Instance: {}
337
338 Called By: {}::{}
339 At: {}:{}
340 Thread: {}
341
342 Current Owner: {}
343 #Of Waiters: {}
344 Exclusive Waiter: {}
345
346 Latest Acquisition By: {}::{}
347 At: {}:{}
348 Thread: {}
349 Latest Release By: {}::{}
350 At: {}:{}
351 Thread: {}
352
353 Latest Wait By: {}::{}
354 At: {}:{}
355 Thread: {}
356 Latest Notify By: {}::{}
357 At: {}:{}
358 Thread: {}
359 \endverbatim
360*/
362 static const char* ASSERTION_FORMAT;
363
364 /// Writes assertion info and calls #"ALIB_ASSERT".
365 /// @see
366 /// Inherited field #"DbgLockAsserter::ASSERTION_FORMAT" which allows changing the output
367 /// format to achieve 'clickable' assertion messages.
368 /// @param cond The condition that is to be met.
369 /// @param assertCI Location where the assertion is placed.
370 /// @param ci Location of the call to the method that asserted.
371 /// @param headline The message.
373 void Assert( bool cond, const CallerInfo& assertCI, const CallerInfo& ci,
374 const char* headline );
375
376 /// Returns \c true if the current owner is the current thread.<p>
377 /// Available only in debug-builds.
378 /// @return \c true, if the lock is owned by this thread, \c false if it is owned by
379 /// another thread or not owned.
381 { return Owner != nullptr && Owner == Thread::GetCurrent() ; }
382
383}; // struct DbgConditionAsserter
384
385} // namespace [alib::threads]
386
387#endif // !ALIB_SINGLE_THREADED && ALIB_DEBUG
#define ALIB_DLL
Definition alib.inl:573
#define ALIB_CALLER
Definition alib.inl:1096
#define ALIB_EXPORT
Definition alib.inl:562
static Thread * GetCurrent()
Definition thread.inl:298
lang::CallerInfo CallerInfo
Type alias in namespace alib.
characters::character character
Type alias in namespace alib.
std::thread::id ThreadID
The ID of the calling thread.
void Assert(bool cond, const CallerInfo &assertCI, const CallerInfo &ci, const char *headline)
CallerInfo NotifyCI
The most recent call to ReleaseAndNotify or.
const character * Name
The name of this instance.
CallerInfo RelCI
Source location of the most recent release.
Thread * Owner
Tracks the current owner.
std::atomic< int > CntWaiters
The number of currently waiting threads.
CallerInfo AcqCI
Source location of the most recent acquirement.
CallerInfo WaitCI
The most recent call to WaitForNotification.
void AssertOwned(const CallerInfo &assertCI, const CallerInfo &ci)
static const char * ASSERTION_FORMAT
CallerInfo RelCI
Source location of the most recent release.
virtual void DoAssert(int type, const CallerInfo &assertCI, const CallerInfo &ci, const char *headline)
void AssertNotOwning(const CallerInfo &assertCI, const CallerInfo &ci, const char *headline)
std::atomic< int > CntAcquirements
The number of shared acquirements.
bool WillRelease() const noexcept
void SetRecursiveOwner(const CallerInfo &assertCI, const CallerInfo &requestCI)
CallerInfo AcqCI
Source location of the most recent acquirement.
virtual ~DbgLockAsserter()
Destructor.
void Release(const CallerInfo &assertCI, const CallerInfo &requestCI)
void SetOwner(const CallerInfo &assertCI, const CallerInfo &requestCI)
CallerInfo SAcqCI
The most recent shared acquirement's caller.
virtual ~DbgSharedLockAsserter() override
Destructor.
void DoAssert(int type, const CallerInfo &assertCI, const CallerInfo &ci, const char *headline) override
void SetOwner(const CallerInfo &assertCI, const CallerInfo &requestCI)
static const char * ASSERTION_FORMAT_SHARED
void SetSharedOwner(const CallerInfo &assertCI, const CallerInfo &requestCI, int warnLimit)
std::atomic< int > CntSharedAcquirements
The number of shared acquirements.
void ReleaseShared(const CallerInfo &assertCI, const CallerInfo &requestCI)
CallerInfo SRelCI
The most recent shared release caller.