ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
dbgcriticalsections.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_lang of the \aliblong.
4///
5/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8ALIB_EXPORT namespace alib::lang {
9
10/// This external variable is available only if the configuration macro
11/// #"ALIB_DEBUG_CRITICAL_SECTIONS" is set and module \alib_threads is included in the
12/// \alibbuild_nl.<br>
13/// When it is \c 0, which is the default, nothing is done.<br>
14/// When it is \c 1, then #"Thread::YieldToSystem;*" is invoked with interface methods
15/// of class #"lang::DbgCriticalSections".<br>
16/// Other values are passed to a call to #"Thread::SleepNanos;*".
17///
18/// The purpose of this debug-feature is to be better able to detect non-protected concurrent
19/// access to critical sections. With increasing the time slice that a thread remains in
20/// a critical section, the probability to be caught by another thread is increased.
22
23
24/// This class supports detecting a racing condition in multithreaded applications.
25/// For this, entering and exiting of critical sections is tracked in either "write" or "read-only"
26/// mode.
27/// The interface methods are named in accordance with types #"threads::Lock" and its siblings
28/// (#"threads::RecursiveLock", #"threads::SharedLock", and so on).
29/// With that, this class in compatible and usable with class #"lang::Owner"
30///
31/// With the use of atomic counters, both pairs of acquirement are reentrant, which simplifies
32/// the use of this type. Furthermore, it is allowed to gain read access by the same thread
33/// that already has acquired write access. The reverse is not allowed: If first read access was
34/// noticed, a later try to acquire write access will be asserted.
35///
36/// The type becomes empty - and thus any call will be optimized out - in case the configuration macro
37/// #"ALIB_DEBUG_CRITICAL_SECTIONS" is not set.
38/// Nevertheless, it is recommended to exclusively use the
39/// \ref alib_macros_mod_threads "ALib Module Threads Macros" when using this type, as those
40/// fully guarantee that any use of this type is pruned with release compilations.
41/// Only in exclamatory cases, these macros may not be flexible enough for use.
42///
43/// In case critical sections that are protected using this class are in fact protected by
44/// one of the #"alib_threads_locks;ALib mutex types", it can furthermore be asserted that
45/// such mutex is acquired when a section is entered.
46/// For this, assign the instance to the field #"DCSLock".
47/// A lock-instance may be assigned to more than one #"%lang::DbgCriticalSections" instance.
48///
49/// If the configuration macro \b ALIB_DEBUG_CRITICAL_SECTIONS is set, besides counting owners and
50/// readers, and raising corresponding assertions, the type can be enabled to simulate some workload
51/// on the using machine. For this, a thread can be forced to either yield to the system or even
52/// to sleep a given number of nanoseconds before continuing execution.
53/// This increases the probability of (detecting) racing conditions.<br>
54/// To activate this feature for all instances, the namespace variable
55/// #"DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS" is to be adjusted. Per-instance
56/// adjustments can be made by setting the field #"DCSYieldOrSleepTimeInNS".
57///
58/// The output format of assertions should be 'clickable' inside a users' IDE.
59/// The default output string is optimized for
60/// \https{JetBrains CLion,www.jetbrains.com/clion} and can be changed by manipulating
61/// the member #".ASSERTION_FORMAT".
62///
63/// @see Chapter #"alib_threads_intro_assert" of the Programmer's Manual of the module
64/// \alib_threads.
66#if ALIB_DEBUG_CRITICAL_SECTIONS
67 /// Virtual class that (usually) holds a lockable type and checks if it is locked or
68 /// shared-locked.
69 /// An instance of this type may be assigned to a #"%lang::DbgCriticalSections" instance.<br>
70 /// Implementations for the six lock types exists. Custom implementations may also be given.
71 /// This is done, for example, in with class #"ThreadPool" of sibling module \alib_threadmodel.
73 /// Virtual Destructor
74 virtual ~AssociatedLock() {}
75
76 /// @return \c true if the lock is acquired (in non-shared mode), \c false otherwise.
77 virtual bool DCSIsAcquired() const =0;
78
79 /// @return \c true if the lock is shared-acquired (by at least any thread).
80 /// Otherwise, returns \c false.
81 virtual bool DCSIsSharedAcquired() const =0;
82 }; // struct AssociatedLock
83
84 /// The name of this DCS. Used for debug-output.
85 const char* DCSName;
86
87 /// If positive, the value found here, overwrites what is given with namespace variable
88 /// #"DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS".<br>
89 /// Defaults to \c -1.
91
92 mutable std::atomic<int> DCSWriterCnt{0}; ///< Tracks enter/exit calls (including readers)
93 mutable std::atomic<int> DCSReaderCnt{0}; ///< Tracks enter/exit calls of readers.
94 mutable CallerInfo DCSAcq ; ///< Source location of acquirement.
95 mutable CallerInfo DCSRel ; ///< Source location of the last "reader" seen.
96 mutable CallerInfo DCSSAcq ; ///< Source location of acquirement.
97 mutable CallerInfo DCSSRel ; ///< Source location of the last "reader" seen.
98
99 /// A union of pointers to different lock types.
100 /// Those can optionally be attached to be checked whether a lock is duly acquired.
102
103 /// The format string used to write exceptions to the console.
104 /// This string can be changed if the source information is not "clickable" in a user's
105 /// development environment.<br>
106 ///
107 /// The default string is optimized for
108 /// \https{JetBrains CLion,www.jetbrains.com/clion} and is defined as:
109 /** \verbatim
110Assertion in Critical Section \"{}\"
111 Message: {}
112 In (Member-)Function: {}
113 Is Owned: {} ({})
114 Is Shared Owned: {} ({})
115
116 Called By: {}::{}
117 At: {}:{}
118 Thread: {}
119
120 Latest Acquisition By: {}::{}
121 At: {}:{}
122 Thread: {}
123 Latest Release By: {}::{}
124 At: {}:{}
125 Thread: {}
126
127 Latest Shared Acquisition By: {}::{}
128 At: {}:{}
129 Thread: {}
130 Latest SharedRelease By: {}::{}
131 At: {}:{}
132 Thread: {}
133 \endverbatim
134 */
136 static const char* ASSERTION_FORMAT;
137
138 //################################################################################################
139 // internals/helpers
140 //################################################################################################
141
142 /// Evaluates #"DCSYieldOrSleepTimeInNS", respectively, if this is negative,
143 /// #"DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS". For value
144 /// - \c 0, nothing is done, for
145 /// - \c 1, a yield into the system is done, and for
146 /// - values greater than \c 1, the calling thread sleeps for the corresponding number of
147 /// nanoseconds sleep time.
148 void yieldOrSleep() const {
151 if ( yieldOrSleep == 1) std::this_thread::yield();
152 else if ( yieldOrSleep >= 2) std::this_thread::sleep_for( std::chrono::nanoseconds( yieldOrSleep ) );
153 }
154
155 /// Asserts the condition and if \c false, #"alib_mod_assert;raises an ALib error".
156 /// @param cond The condition to assert.
157 /// @param ciAssert Caller information of the assertion in this class.
158 /// @param ci Caller information.
159 /// @param headline The problem that occurred.
161 void doAssert( bool cond, const CallerInfo& ciAssert, const CallerInfo& ci,
162 const char* headline ) const;
163
164
165 //################################################################################################
166 // Constructor/Destructor
167 //################################################################################################
168 /// Constructor.
169 /// @param name The name to display with assertions.
170 DbgCriticalSections(const char* name) : DCSName(name) {}
171
172 /// Destructor. Checks that this instance is unused.
174 {
175 doAssert(DCSWriterCnt.load() == 0, ALIB_CALLER, ALIB_CALLER, "Destructor called while still owned" );
176 doAssert(DCSReaderCnt.load() == 0, ALIB_CALLER, ALIB_CALLER, "Destructor called while still owned (shared)" );
177 }
178
179
180 //################################################################################################
181 // Interface
182 //################################################################################################
183
184 /// Increases the #"DCSWriterCnt" and checks for potential assertions.
185 /// @param ci Caller information.
187 void Acquire ( const CallerInfo& ci ) const;
188
189 /// Decreases the #"DCSWriterCnt" and checks for potential assertions.
190 /// @param ci Caller information.
192 void Release ( const CallerInfo& ci ) const;
193
194 /// Increases #"DCSReaderCnt" and checks for potential assertions.
195 /// @param ci Caller information.
197 void AcquireShared( const CallerInfo& ci ) const;
198
199 /// Decreases #"DCSReaderCnt" and checks for potential assertions.
200 /// @param ci Caller information.
202 void ReleaseShared( const CallerInfo& ci ) const;
203
204#else // ALIB_DEBUG_CRITICAL_SECTIONS
205 void Acquire ( const lang::CallerInfo& ) const {}
206 void Release ( const lang::CallerInfo& ) const {}
207 void AcquireShared( const lang::CallerInfo& ) const {}
208 void ReleaseShared( const lang::CallerInfo& ) const {}
209#endif // ALIB_DEBUG_CRITICAL_SECTIONS
210};
211
212
213} // namespace [alib::lang]
#define ALIB_DLL
#define ALIB_CALLER
#define ALIB_EXPORT
unsigned DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS
CallerInfo DCSSRel
Source location of the last "reader" seen.
void Acquire(const CallerInfo &ci) const
void doAssert(bool cond, const CallerInfo &ciAssert, const CallerInfo &ci, const char *headline) const
CallerInfo DCSRel
Source location of the last "reader" seen.
const char * DCSName
The name of this DCS. Used for debug-output.
CallerInfo DCSSAcq
Source location of acquirement.
CallerInfo DCSAcq
Source location of acquirement.
void Release(const CallerInfo &ci) const
void AcquireShared(const CallerInfo &ci) const
std::atomic< int > DCSReaderCnt
Tracks enter/exit calls of readers.
void ReleaseShared(const CallerInfo &ci) const
std::atomic< int > DCSWriterCnt
Tracks enter/exit calls (including readers).
~DbgCriticalSections()
Destructor. Checks that this instance is unused.