ALib C++ Framework
by
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
dbgcriticalsections.inl
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/// \emoji :copyright: 2013-2025 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 \b instance to the field #"DCSLock".
47/// A lock-instance may be assigned to more than one \b 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 \b 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 {
74 /// Virtual Destructor
75 virtual ~AssociatedLock() {}
76
77 /// @return \c true if the lock is acquired (in non-shared mode), \c false otherwise.
78 virtual bool DCSIsAcquired() const =0;
79
80 /// @return \c true if the lock is shared-acquired (by at least any thread).
81 /// Otherwise, returns \c false.
82 virtual bool DCSIsSharedAcquired() const =0;
83 }; // struct AssociatedLock
84
85 /// The name of this DCS. Used for debug-output.
86 const char* DCSName;
87
88 /// If positive, the value found here, overwrites what is given with namespace variable
89 /// #"DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS".<br>
90 /// Defaults to \c -1.
92
93 mutable std::atomic<int> DCSWriterCnt{0}; ///< Tracks enter/exit calls (including readers)
94 mutable std::atomic<int> DCSReaderCnt{0}; ///< Tracks enter/exit calls of readers.
95 mutable CallerInfo DCSAcq ; ///< Source location of acquirement.
96 mutable CallerInfo DCSRel ; ///< Source location of the last "reader" seen.
97 mutable CallerInfo DCSSAcq ; ///< Source location of acquirement.
98 mutable CallerInfo DCSSRel ; ///< Source location of the last "reader" seen.
99
100 /// A union of pointers to different lock types.
101 /// Those can optionally be attached to be checked whether a lock is duly acquired.
103
104 /// The format string used to write exceptions to the console.
105 /// This string can be changed if the source information is not "clickable" in a user's
106 /// development environment.<br>
107 ///
108 /// The default string is optimized for
109 /// \https{JetBrains CLion,www.jetbrains.com/clion} and is defined as:
110 /** \verbatim
111Assertion in Critical Section \"{}\"
112 Message: {}
113 In (Member-)Function: {}
114 Is Owned: {} ({})
115 Is Shared Owned: {} ({})
116
117 Called By: {}::{}
118 At: {}:{}
119 Thread: {}
120
121 Latest Acquisition By: {}::{}
122 At: {}:{}
123 Thread: {}
124 Latest Release By: {}::{}
125 At: {}:{}
126 Thread: {}
127
128 Latest Shared Acquisition By: {}::{}
129 At: {}:{}
130 Thread: {}
131 Latest SharedRelease By: {}::{}
132 At: {}:{}
133 Thread: {}
134 \endverbatim
135 */
137 static const char* ASSERTION_FORMAT;
138
139 //################################################################################################
140 // internals/helpers
141 //################################################################################################
142
143 /// Evaluates #"DCSYieldOrSleepTimeInNS", respectively, if this is negative,
144 /// #"DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS". For value
145 /// - \c 0, nothing is done, for
146 /// - \c 1, a yield into the system is done, and for
147 /// - values greater than \c 1, the calling thread sleeps for the corresponding number of
148 /// nanoseconds sleep time.
149 void yieldOrSleep() const {
152 if ( yieldOrSleep == 1) std::this_thread::yield();
153 else if ( yieldOrSleep >= 2) std::this_thread::sleep_for( std::chrono::nanoseconds( yieldOrSleep ) );
154 }
155
156 /// Asserts the condition and if \c false, #"alib_mod_assert;raises an ALib error".
157 /// @param cond The condition to assert.
158 /// @param ciAssert Caller information of the assertion in this class.
159 /// @param ci Caller information.
160 /// @param headline The problem that occurred.
162 void doAssert( bool cond, const CallerInfo& ciAssert, const CallerInfo& ci,
163 const char* headline ) const;
164
165
166 //################################################################################################
167 // Constructor/Destructor
168 //################################################################################################
169 /// Constructor.
170 /// @param name The name to display with assertions.
171 DbgCriticalSections(const char* name) : DCSName(name) {}
172
173 /// Destructor. Checks that this instance is unused.
175 {
176 doAssert(DCSWriterCnt.load() == 0, ALIB_CALLER, ALIB_CALLER, "Destructor called while still owned" );
177 doAssert(DCSReaderCnt.load() == 0, ALIB_CALLER, ALIB_CALLER, "Destructor called while still owned (shared)" );
178 }
179
180
181 //################################################################################################
182 // Interface
183 //################################################################################################
184
185 /// Increases the #"DCSWriterCnt" and checks for potential assertions.
186 /// @param ci Caller information.
188 void Acquire ( const CallerInfo& ci ) const;
189
190 /// Decreases the #"DCSWriterCnt" and checks for potential assertions.
191 /// @param ci Caller information.
193 void Release ( const CallerInfo& ci ) const;
194
195 /// Increases #"DCSReaderCnt" and checks for potential assertions.
196 /// @param ci Caller information.
198 void AcquireShared( const CallerInfo& ci ) const;
199
200 /// Decreases #"DCSReaderCnt" and checks for potential assertions.
201 /// @param ci Caller information.
203 void ReleaseShared( const CallerInfo& ci ) const;
204
205#else // ALIB_DEBUG_CRITICAL_SECTIONS
206 void Acquire ( const lang::CallerInfo& ) const {}
207 void Release ( const lang::CallerInfo& ) const {}
208 void AcquireShared( const lang::CallerInfo& ) const {}
209 void ReleaseShared( const lang::CallerInfo& ) const {}
210#endif // ALIB_DEBUG_CRITICAL_SECTIONS
211};
212
213
214} // namespace [alib::lang]
#define ALIB_DLL
Definition alib.inl:573
#define ALIB_CALLER
Definition alib.inl:1096
#define ALIB_EXPORT
Definition alib.inl:562
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.