ALib C++ Library
Library Version: 2412 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_threads of the \aliblong.
4///
5/// \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
6/// Published under \ref mainpage_license "Boost Software License".
7//==================================================================================================
8#ifndef HPP_ALIB_LANG_DBGCRITICALSECTIONS
9#define HPP_ALIB_LANG_DBGCRITICALSECTIONS 1
10#pragma once
11#if !defined(DOXYGEN)
12# include "alib/alib.hpp"
13#endif
14#include "alib/lang/owner.hpp"
15
16#if ALIB_DEBUG_CRITICAL_SECTIONS
17# include <thread>
18# include <atomic>
19#endif
20
21namespace alib::threads
22{
23 class Lock; class TimedLock;
25 class SharedLock; class SharedTimedLock;
26}
27
28namespace alib::lang {
29#if ALIB_DEBUG && ALIB_THREADS && ALIB_DEBUG_CRITICAL_SECTIONS
30
31/// This external variable is available only if the compiler symbol
32/// \ref ALIB_DEBUG_CRITICAL_SECTIONS is set and module \alib_threads is included in the
33/// \alibdist_nl.<br>
34/// When it is \c 0, which is the default, nothing is done.<br>
35/// When it is \c 1, then \alib{threads;Thread::YieldToSystem} is invoked with interface methods
36/// of class \alib{lang;DbgCriticalSections}.<br>
37/// Other values are passed to a call to \alib{threads;Thread::SleepNanos}.
38///
39/// The purpose of this debug-feature is to be better able to detect non-protected concurrent
40/// access to critical sections. With increasing the time slice that a thread remains in
41/// a critical section, the probability to be caught by another thread is increased.
43
44# define ALIB_DCS ALIB_OWN(*this)
45# define ALIB_DCS_SHARED ALIB_OWN_SHARED(*this)
46# define ALIB_DCS_WITH(CS) ALIB_OWN(CS)
47# define ALIB_DCS_SHARED_WITH(CS) ALIB_OWN_SHARED(CS)
48# define ALIB_DCS_ACQUIRE Acquire(ALIB_CALLER);
49# define ALIB_DCS_RELEASE Release(ALIB_CALLER);
50# define ALIB_DCS_ACQUIRE_WITH(CS) CS.Acquire(ALIB_CALLER);
51# define ALIB_DCS_RELEASE_WITH(CS) CS.Release(ALIB_CALLER);
52# define ALIB_DCS_ACQUIRE_SHARED AcquireShared(ALIB_CALLER);
53# define ALIB_DCS_RELEASE_SHARED ReleaseShared(ALIB_CALLER);
54# define ALIB_DCS_ACQUIRE_SHARED_WITH(CS) CS.AcquireShared(ALIB_CALLER);
55# define ALIB_DCS_RELEASE_SHARED_WITH(CS) CS.ReleaseShared(ALIB_CALLER);
56
57#else
58# define ALIB_DCS { alib::DbgAssertSingleThreaded(); }
59# define ALIB_DCS_SHARED { alib::DbgAssertSingleThreaded(); }
60# define ALIB_DCS_WITH(...) { alib::DbgAssertSingleThreaded(); }
61# define ALIB_DCS_RECURSIVE_WITH(...) { alib::DbgAssertSingleThreaded(); }
62# define ALIB_DCS_SHARED_WITH(...) { alib::DbgAssertSingleThreaded(); }
63# define ALIB_DCS_ACQUIRE { alib::DbgAssertSingleThreaded(); }
64# define ALIB_DCS_RELEASE {}
65# define ALIB_DCS_ACQUIRE_WITH(CS) { alib::DbgAssertSingleThreaded(); }
66# define ALIB_DCS_RELEASE_WITH(CS) {}
67# define ALIB_DCS_ACQUIRE_SHARED { alib::DbgAssertSingleThreaded(); }
68# define ALIB_DCS_RELEASE_SHARED {}
69# define ALIB_DCS_ACQUIRE_SHARED_WITH(CS) { alib::DbgAssertSingleThreaded(); }
70# define ALIB_DCS_RELEASE_SHARED_WITH(CS) {}
71#endif
72
73/// This class supports detecting a racing condition in multithreaded applications.
74/// For this, entering and exiting of critical sections is tracked in either "write" or "read only"
75/// mode.
76/// The interface methods are named in accordance with types \alib{lang;Owner} (#Acquire/#Release)
77/// and \alib{lang;OwnerShared} (#AcquireShared/#ReleaseShared).
78///
79/// With the use of atomic counters, both pairs of acquirement are reentrant, which simplifies
80/// the use of this type. Furthermore, it is allowed to gain read access after write access by
81/// the same thread was acquired. The reverse is not allowed: If the first read access was
82/// registered, a subsequent write access will be asserted.
83///
84/// The type becomes empty, and thus any call will be optimized out in case the compiler symbol
85/// \ref ALIB_DEBUG_CRITICAL_SECTIONS is not set.
86/// Nevertheless, it is recommended to exclusively use the
87/// \ref GrpALibMacros_mod_threads "ALib Module Threads Macros" when using this type, as those
88/// fully guarantee that any use of this type is pruned with release compilations.
89/// Only in exclamatory cases, these macros may not be flexible enough for use.
90///
91/// In case critical sections that are protected using this class are in fact protected by
92/// one of the \ref alib_threads_locks "ALib mutex types", it can furthermore be asserted that
93/// such mutex is acquired when a section is entered.
94/// For this, assign the \b instance to field #DCSLock.
95/// A lock-instance may be assigned to more than one \b DbgCriticalSections instance.
96///
97/// If the compiler symbol \b ALIB_DEBUG_CRITICAL_SECTIONS is set, besides counting owners and
98/// readers, and raising corresponding assertions, the type can be enabled to simulate some workload
99/// on the using machine. For this, a thread can be forced to either yield to the system or even
100/// to sleep a given number of nanoseconds before continuing execution.
101/// This increases the probability of (detecting) racing conditions.<br>
102/// To activate this feature for all instances, namespace variable
103/// \alib{lang;DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS} is to be adjusted. Per-instance
104/// adjustments can be made by setting the field #DCSYieldOrSleepTimeInNS.
105///
106/// The output format of assertions should be 'clickable' inside a users' IDE.
107/// The default output string is optimized for
108/// \https{JetBrains CLion,www.jetbrains.com/clion} and can be changed by manipulating
109/// the member #ASSERTION_FORMAT.
110///
111/// @see Chapter \ref alib_threads_intro_assert of the Programmer's Manual of module \alib_threads.
113{
114#if ALIB_DEBUG_CRITICAL_SECTIONS
115 /// Virtual class that (usually) holds a lockable type and checks if it is locked or
116 /// shared-locked.
117 /// An instance of this type may be assigned to a \b DbgCriticalSections instance.<br>
118 /// Implementations for the six lock types exists. Custom implementations may of course be
119 /// given. This is done, for example, in with class \alib{threadmodel;ThreadPool} of sibling
120 /// module \alib_threadmodel.
122 {
123 /// Virtual Destructor
124 virtual ~AssociatedLock() {}
125
126 /// @return \c true if the lock is acquired (in non-shared mode), \c false otherwise.
127 virtual bool DCSIsAcquired() const = 0;
128
129 /// @return \c true if the lock is shared-acquired (by at least any thread).
130 /// Otherwise, returns \c false.
131 virtual bool DCSIsSharedAcquired() const = 0;
132 }; // struct AssociatedLock
133
134 /// The name of this DCS. Used for debug-output.
135 const char* DCSName;
136
137 /// If positive, the value found here, overwrites what is given with namespace variable
138 /// \alib{lang;DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS}.<br>
139 /// Defaults to \c -1.
141
142 mutable std::atomic<int> DCSWriterCnt{0}; ///< Tracks enter/exit calls (including readers)
143 mutable std::atomic<int> DCSReaderCnt{0}; ///< Tracks enter/exit calls of readers.
144 mutable CallerInfo DCSAcq ; ///< Source location of acquirement.
145 mutable CallerInfo DCSRel ; ///< Source location of the last "reader" seen.
146 mutable CallerInfo DCSSAcq ; ///< Source location of acquirement.
147 mutable CallerInfo DCSSRel ; ///< Source location of the last "reader" seen.
148
149 /// A union of pointers to different lock types.
150 /// Those can optionally be attached to be checked whether a lock is duly acquired.
152
153#if ALIB_CAMP
154 /// The format string used to write exceptions to the console.
155 /// This string can be changed if the source information is not "clickable" in a user's
156 /// development environment.<br>
157 ///
158 /// The default string is optimized for
159 /// \https{JetBrains CLion,www.jetbrains.com/clion} and is defined as:
160 /** \verbatim
161Assertion in Critical Section {!Q}
162 Message: {}
163 In (Member-)Function: {2:ya}
164 Is Owned: {6}
165 Is Shared Owned: {9}
166
167 Called By: {3:ya}
168 At: {3:sf:sl}
169 Thread: {3:ta}
170
171 Latest Acquisition By: {4:ya}
172 At: {4:sf:sl}
173 Thread: {4:ta}
174 Latest Release By: {5:ya}
175 At: {5:sf:sl}
176 Thread: {5:ta}
177
178 Latest Shared Acquisition By: {7:ya}
179 At: {7:sf:sl}
180 Thread: {7:ta}
181 Latest SharedRelease By: {8:ya}
182 At: {8:sf:sl}
183 Thread: {8:ta}
184 \endverbatim
185 <p>
186 The placeholder fields that this format string refers to are set as follows:
187
188 - \c 0: Debug-name of the critical section.
189 - \c 1: Headline.
190 - \c 2: \b %CallerInfo of assertion.
191 - \c 3: \b %CallerInfo of caller.
192 - \c 4: \b %CallerInfo of latest acquisition.
193 - \c 5: \b %CallerInfo of latest release.
194 - \c 6: Acquirement information string
195 - \c 7: \b %CallerInfo of latest shared acquisition.
196 - \c 8: \b %CallerInfo of latest shared release.
197 - \c 9: Shared acquirement information string.
198
199 The format specification of the type \c CallerInfo is defined with class
200 \alib{lang::format;FMTCallerInfo}.
201 \par Availability:
202 This field is only available with the inclusion of \alib_basecamp in the \alibdist.<br> */
204 static const char* ASSERTION_FORMAT;
205#endif
206
207 // ###############################################################################################
208 // internals/helpers
209 // ###############################################################################################
210
211 /// Evaluates #DCSYieldOrSleepTimeInNS, respectively, if this is negative,
212 /// \alib{lang;DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS}. For value
213 /// - \c 0, nothing is done, for
214 /// - \c 1, a yield into the system is done, and for
215 /// - values greater than \c 1, the calling thread sleeps for the corresponding number of
216 /// nanoseconds sleep time.
218 void yieldOrSleep() const
219 {
222 if ( yieldOrSleep == 1) std::this_thread::yield();
223 else if ( yieldOrSleep >= 2) std::this_thread::sleep_for( std::chrono::nanoseconds( yieldOrSleep ) );
224 }
225
226 /// Raises an \alib assertion.
227 /// @param cond The condition to assert.
228 /// @param ciAssert Caller information of the assertion in this class.
229 /// @param ci Caller information.
230 /// @param headline The problem that occurred.
232 void doAssert( bool cond, const CallerInfo& ciAssert, const CallerInfo& ci,
233 const char* headline ) const;
234
235
236 // ###############################################################################################
237 // Constructor/Destructor
238 // ###############################################################################################
239 /// Constructor.
240 /// @param name The name to display with assertions.
242 DbgCriticalSections(const char* name) : DCSName(name) {}
243 /// Destructor. Checks that this instance is unused.
246 {
247 doAssert(DCSWriterCnt.load() == 0, ALIB_CALLER, ALIB_CALLER, "Destructor called while still owned" );
248 doAssert(DCSReaderCnt.load() == 0, ALIB_CALLER, ALIB_CALLER, "Destructor called while still owned (shared)" );
249 }
250
251
252 // ###############################################################################################
253 // Interface
254 // ###############################################################################################
255
256 /// Increases the #DCSWriterCnt and checks for potential assertions.
257 /// @param ci Caller information.
259 void Acquire ( const CallerInfo& ci ) const;
260
261 /// Decreases the #DCSWriterCnt and checks for potential assertions.
262 /// @param ci Caller information.
264 void Release ( const CallerInfo& ci ) const;
265
266 /// Increases #DCSReaderCnt and checks for potential assertions.
267 /// @param ci Caller information.
269 void AcquireShared( const CallerInfo& ci ) const;
270
271 /// Decreases #DCSReaderCnt and checks for potential assertions.
272 /// @param ci Caller information.
274 void ReleaseShared( const CallerInfo& ci ) const;
275
276#else // ALIB_DEBUG_CRITICAL_SECTIONS
277 ALIB_FORCE_INLINE void Acquire ( const lang::CallerInfo& ) const {}
278 ALIB_FORCE_INLINE void Release ( const lang::CallerInfo& ) const {}
279 ALIB_FORCE_INLINE void AcquireShared( const lang::CallerInfo& ) const {}
280 ALIB_FORCE_INLINE void ReleaseShared( const lang::CallerInfo& ) const {}
281#endif // ALIB_DEBUG_CRITICAL_SECTIONS
282};
283
284
285} // namespace [alib::lang]
286
287#endif // HPP_ALIB_LANG_DBGCRITICALSECTIONS
288
#define ALIB_CALLER
Definition alib.hpp:1164
#define ALIB_FORCE_INLINE
Definition alib.hpp:650
#define ALIB_API
Definition alib.hpp:639
unsigned int DBG_CRITICAL_SECTION_YIELD_OR_SLEEP_TIME_IN_NS
Definition alib.cpp:298
threads::SharedTimedLock SharedTimedLock
Type alias in namespace alib.
threads::TimedLock TimedLock
Type alias in namespace alib.
threads::Lock Lock
Type alias in namespace alib.
Definition lock.hpp:145
threads::RecursiveLock RecursiveLock
Type alias in namespace alib.
threads::SharedLock SharedLock
Type alias in namespace alib.
threads::RecursiveTimedLock RecursiveTimedLock
Type alias in namespace alib.
ALIB_FORCE_INLINE ~DbgCriticalSections()
Destructor. Checks that this instance is unused.
static ALIB_API const char * ASSERTION_FORMAT
ALIB_API void doAssert(bool cond, const CallerInfo &ciAssert, const CallerInfo &ci, const char *headline) const
std::atomic< int > DCSReaderCnt
Tracks enter/exit calls of readers.
CallerInfo DCSAcq
Source location of acquirement.
ALIB_API void ReleaseShared(const CallerInfo &ci) const
CallerInfo DCSRel
Source location of the last "reader" seen.
ALIB_API void Release(const CallerInfo &ci) const
CallerInfo DCSSAcq
Source location of acquirement.
CallerInfo DCSSRel
Source location of the last "reader" seen.
const char * DCSName
The name of this DCS. Used for debug-output.
ALIB_API void AcquireShared(const CallerInfo &ci) const
ALIB_FORCE_INLINE void yieldOrSleep() const
ALIB_API void Acquire(const CallerInfo &ci) const
ALIB_FORCE_INLINE DbgCriticalSections(const char *name)
std::atomic< int > DCSWriterCnt
Tracks enter/exit calls (including readers)