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