ALib C++ Library
Library Version: 2510 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 \ref mainpage_license "Boost Software License".
7//==================================================================================================
8ALIB_EXPORT namespace alib::lang {
9
10/// This external variable is available only if the compiler-symbol
11/// \ref 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 \alib{threads;Thread::YieldToSystem} is invoked with interface methods
15/// of class \alib{lang;DbgCriticalSections}.<br>
16/// Other values are passed to a call to \alib{threads;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 \alib{lang;Owner} (#Acquire/#Release)
28/// and \alib{lang;OwnerShared} (#AcquireShared/#ReleaseShared).
29///
30/// With the use of atomic counters, both pairs of acquirement are reentrant, which simplifies
31/// the use of this type. Furthermore, it is allowed to gain read access after write access by
32/// the same thread was acquired. The reverse is not allowed: If the first read access was
33/// registered, a subsequent write access will be asserted.
34///
35/// The type becomes empty, and thus any call will be optimized out in case the compiler-symbol
36/// \ref ALIB_DEBUG_CRITICAL_SECTIONS is not set.
37/// Nevertheless, it is recommended to exclusively use the
38/// \ref GrpALibMacros_mod_threads "ALib Module Threads Macros" when using this type, as those
39/// fully guarantee that any use of this type is pruned with release compilations.
40/// Only in exclamatory cases, these macros may not be flexible enough for use.
41///
42/// In case critical sections that are protected using this class are in fact protected by
43/// one of the \ref alib_threads_locks "ALib mutex types", it can furthermore be asserted that
44/// such mutex is acquired when a section is entered.
45/// For this, assign the \b instance to field #DCSLock.
46/// A lock-instance may be assigned to more than one \b DbgCriticalSections instance.
47///
48/// If the compiler-symbol \b ALIB_DEBUG_CRITICAL_SECTIONS is set, besides counting owners and
49/// readers, and raising corresponding assertions, the type can be enabled to simulate some workload
50/// on the using machine. For this, a thread can be forced to either yield to the system or even
51/// to sleep a given number of nanoseconds before continuing execution.
52/// This increases the probability of (detecting) racing conditions.<br>
53/// To activate this feature for all instances, namespace variable
54/// \alib{lang;DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS} is to be adjusted. Per-instance
55/// adjustments can be made by setting the field #DCSYieldOrSleepTimeInNS.
56///
57/// The output format of assertions should be 'clickable' inside a users' IDE.
58/// The default output string is optimized for
59/// \https{JetBrains CLion,www.jetbrains.com/clion} and can be changed by manipulating
60/// the member #ASSERTION_FORMAT.
61///
62/// @see Chapter \ref alib_threads_intro_assert of the Programmer's Manual of module \alib_threads.
64{
65#if ALIB_DEBUG_CRITICAL_SECTIONS
66 /// Virtual class that (usually) holds a lockable type and checks if it is locked or
67 /// shared-locked.
68 /// An instance of this type may be assigned to a \b DbgCriticalSections instance.<br>
69 /// Implementations for the six lock types exists. Custom implementations may of course be
70 /// given. This is done, for example, in with class \alib{threadmodel;ThreadPool} of sibling
71 /// 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 /// \alib{lang;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 /// \alib{lang;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
150 {
153 if ( yieldOrSleep == 1) std::this_thread::yield();
154 else if ( yieldOrSleep >= 2) std::this_thread::sleep_for( std::chrono::nanoseconds( yieldOrSleep ) );
155 }
156
157 /// Asserts the condition and if \c false, \ref alib_mod_assert "raises an ALib error".
158 /// @param cond The condition to assert.
159 /// @param ciAssert Caller information of the assertion in this class.
160 /// @param ci Caller information.
161 /// @param headline The problem that occurred.
163 void doAssert( bool cond, const CallerInfo& ciAssert, const CallerInfo& ci,
164 const char* headline ) const;
165
166
167 // ###############################################################################################
168 // Constructor/Destructor
169 // ###############################################################################################
170 /// Constructor.
171 /// @param name The name to display with assertions.
172 DbgCriticalSections(const char* name) : DCSName(name) {}
173
174 /// Destructor. Checks that this instance is unused.
176 {
177 doAssert(DCSWriterCnt.load() == 0, ALIB_CALLER, ALIB_CALLER, "Destructor called while still owned" );
178 doAssert(DCSReaderCnt.load() == 0, ALIB_CALLER, ALIB_CALLER, "Destructor called while still owned (shared)" );
179 }
180
181
182 // ###############################################################################################
183 // Interface
184 // ###############################################################################################
185
186 /// Increases the #DCSWriterCnt and checks for potential assertions.
187 /// @param ci Caller information.
189 void Acquire ( const CallerInfo& ci ) const;
190
191 /// Decreases the #DCSWriterCnt and checks for potential assertions.
192 /// @param ci Caller information.
194 void Release ( const CallerInfo& ci ) const;
195
196 /// Increases #DCSReaderCnt and checks for potential assertions.
197 /// @param ci Caller information.
199 void AcquireShared( const CallerInfo& ci ) const;
200
201 /// Decreases #DCSReaderCnt and checks for potential assertions.
202 /// @param ci Caller information.
204 void ReleaseShared( const CallerInfo& ci ) const;
205
206#else // ALIB_DEBUG_CRITICAL_SECTIONS
207 void Acquire ( const lang::CallerInfo& ) const {}
208 void Release ( const lang::CallerInfo& ) const {}
209 void AcquireShared( const lang::CallerInfo& ) const {}
210 void ReleaseShared( const lang::CallerInfo& ) const {}
211#endif // ALIB_DEBUG_CRITICAL_SECTIONS
212};
213
214
215} // namespace [alib::lang]
216
217
#define ALIB_DLL
Definition alib.inl:496
#define ALIB_CALLER
Definition alib.inl:1001
#define ALIB_EXPORT
Definition alib.inl:488
unsigned int DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS
CallerInfo DCSSRel
Source location of the last "reader" seen.
ALIB_DLL void Acquire(const CallerInfo &ci) const
ALIB_DLL 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.
static ALIB_DLL const char * ASSERTION_FORMAT
CallerInfo DCSSAcq
Source location of acquirement.
CallerInfo DCSAcq
Source location of acquirement.
ALIB_DLL void Release(const CallerInfo &ci) const
ALIB_DLL void AcquireShared(const CallerInfo &ci) const
std::atomic< int > DCSReaderCnt
Tracks enter/exit calls of readers.
ALIB_DLL void ReleaseShared(const CallerInfo &ci) const
std::atomic< int > DCSWriterCnt
Tracks enter/exit calls (including readers)
~DbgCriticalSections()
Destructor. Checks that this instance is unused.