ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
threadlock.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_THREADS_THREADLOCK
9#define HPP_ALIB_THREADS_THREADLOCK 1
10
11#if !defined (HPP_ALIB_THREADS_THREADLOCKNR)
13#endif
14
15#if !defined (_GLIBCXX_CONDITION_VARIABLE) && !defined(_CONDITION_VARIABLE_)
16 #include <condition_variable>
17#endif
18
19#if !defined (_GLIBCXX_THREAD) && !defined (_THREAD_ )
20# include <thread>
21#endif
22
23
24namespace alib { namespace threads {
25
26// forwards
27class Thread;
28namespace detail { ALIB_API Thread* getThread(std::thread::id c11ID ); }
29
30/** ************************************************************************************************
31 * While this class does not inherit from \alib{threads;ThreadLockNR}, it copies and extends its
32 * interface and functionality.
33 * With this lock, nested acquisitions are supported with this type.
34 * An instance of this class is released when an equal amount of invocations to #Acquire and
35 * #Release have been performed.
36 *
37 * The object stores the actual owning thread and this thread may be queried. Such
38 * queries are \b not thread safe and should be performed only to perform tasks that are
39 * not mission critical, for example to create log output, usage statistics or similar things.
40 * In other words, a software's algorithmic logic should by principle never use information about
41 * the thread that currently owns a lock.
42 *
43 * With debug builds, a warning threshold for the number of repeated acquisitions can be
44 * defined with public member #DbgRecursionWarningThreshold. As the member's name indicates, it
45 * is \e assumed that too many repeated locks are caused by a recursive calls. Usually, locking
46 * data access should not be done in recursive code.<br>
47 * Furthermore, field #DbgWarningAfterWaitTimeInMillis enables the raise of \alib warnings in case
48 * certain wait time is exceeded. Along with the warning, the owner and waiting threads' names
49 * and IDs are given, along with both source code location of the acquisition, respectively, the
50 * failed acquistion.
51 **************************************************************************************************/
53{
54 // #############################################################################################
55 // Protected fields
56 // #############################################################################################
57 protected:
58 /** Thread ID of the current owner. */
59 std::thread::id owner;
60
61 /** The internal object to lock on. */
62 mutable
63 std::mutex mutex;
64
65 /** The internal object to lock on. */
66 std::condition_variable mutexNotifier;
67
68 /** Counter for the number of Acquire() calls of the current thread. */
69 uint16_t cntAcquirements =0;
70
71 /** The safeness setting. */
73
74 // #############################################################################################
75 // Public fields
76 // #############################################################################################
77 public:
78 #if ALIB_DEBUG
79 /**
80 * This is a threshold that causes Acquire() to raise an \alib warning in debug builds,
81 * if acquiring this lock takes longer than the given number of milliseconds.
82 * Such warning is often a quick first hint for a racing condition.
83 *
84 * To disable such messages, set this value to 0.
85 * The default value is <b>2,000</b> (two seconds), which seems "very long", but can
86 * happen on systems with heavy load.
87 */
89
90 /** Source location of acquirement. (Available only in debug-builds.). */
92
93 /** Source location of acquirement. (Available only in debug-builds.). */
95
96 /** Source location of acquirement. (Available only in debug-builds.). */
98
99 /**
100 * Limit of recursions. If limit is reached or a multiple of it, an error is passed
101 * to \alib{lang;ReportWriter}.
102 * Defaults is \c 10. To disable, set to \c 0.
103 * Available only in debug versions of \alib.
104 */
106 #endif
107
108 // #############################################################################################
109 // Constructors
110 // #############################################################################################
111 public:
112
113 /** ****************************************************************************************
114 * Constructor.
115 *
116 * @param safeness (Optional) Defaults to \c Safeness::Safe.
117 * See #SetSafeness for more information.
118 ******************************************************************************************/
121
122 /** ****************************************************************************************
123 * Destructor.
124 ******************************************************************************************/
126
127 // #############################################################################################
128 // Interface
129 // #############################################################################################
130 public:
131
132 /** ****************************************************************************************
133 * Thread which invokes this method gets registered as the current owner of this object,
134 * until the same thread releases the ownership invoking #Release.
135 * In the case that this object is already owned by another thread, the invoking thread is
136 * suspended until ownership can be gained.
137 * Multiple (nested) calls to this method are counted and the object is only released when
138 * the same number of Release() calls have been made.
139 *
140 * \note
141 * In debug-compilations of the library, this method accepts three parameters,
142 * providing information about the caller. In the release version these parameters do not
143 * exist. Therefore use macro #ALIB_CALLER_PRUNED to provide the parameters:
144 *
145 * sample.Acquire( ALIB_CALLER_PRUNED );
146 *
147 * @param dbgFile Caller information. Available only with debug builds.
148 * @param dbgLine Caller information. Available only with debug builds.
149 * @param dbgFunc Caller information. Available only with debug builds.
150 ******************************************************************************************/
152 #if ALIB_DEBUG
153 void Acquire( const NCString& dbgFile, int dbgLine, const NCString& dbgFunc );
154 #else
155 void Acquire();
156 #endif
157
158 /** ****************************************************************************************
159 * Releases ownership of this object. If #Acquire was called multiple times before, the same
160 * number of calls to this method have to be performed to release ownership.
161 ******************************************************************************************/
163 void Release();
164
165 /** ****************************************************************************************
166 * Returns \c true if the next invocation of #Release will release the lock, otherwise
167 * \c false. In other words, returns \c true if this lock is \ref Acquire "acquired" exactly
168 * \c 1.
169 *
170 * \note
171 * This method is not (and can not) be synchronized. Consequently, a reliable result
172 * is only guaranteed if #IsOwnedByCurrentThread returns \c true.
173 * This method is therefore deemed to be used only in situations where it is assured
174 * that this lock is owned by the current thread.
175 *
176 * @return \c true if locked exactly once.
177 ******************************************************************************************/
178 bool WillRelease() const
179 {
180 return cntAcquirements == 1;
181 }
182
183 /** ****************************************************************************************
184 * Returns the current owner of this lock.
185 * If not acquired, \c nullptr is returned.
186 *
187 * @return The thread that owns this lock.
188 ******************************************************************************************/
190 {
191 return owner == std::this_thread::get_id();
192 }
193
194 /** ****************************************************************************************
195 * Returns the current owner of this lock. If not acquired, \c nullptr is returned.
196 *
197 * \see Method #IsOwnedByCurrentThread.
198 * @return The thread that owns this lock, \c nullptr if not acquired.
199 ******************************************************************************************/
201 {
202 // copying member owner once, makes this method sort of "thread safe"
203 std::thread::id id= owner;
204 if( id== std::thread::id() )
205 return nullptr;
206 return detail::getThread( id );
207 }
208
209 /** ****************************************************************************************
210 * Returns the number of acquirements of this ThreadLock.
211 *
212 * @return The number of (recursive) acquirements.
213 ******************************************************************************************/
215 {
216 return cntAcquirements;
217 }
218
219 /** ****************************************************************************************
220 * If parameter is \c Safeness::Unsafe, the whole locking system is disabled.
221 * The only objective here is to gain execution speed, as thread synchronization causes
222 * relatively expensive system calls.
223 * Use this method only if you are 100% sure that your (otherwise) critical section are
224 * executed in a single threaded environment. And: "relative expensive" means: they are not
225 * really expensive. This is provided only for the rare case that your critical section is
226 * very, very frequently executed.
227 *
228 * @param safeness Determines if this object should use a mutex (\c Safeness::Safe)
229 * or just do nothing (\c Safeness::Unsafe).
230 ******************************************************************************************/
233
234 /** ****************************************************************************************
235 * Query if this instance was set to unsafe mode.
236 * @return A value of type alib::Safeness "Safeness"
237 ******************************************************************************************/
239 {
240 return safeness;
241 }
242};
243
244
245} // namespace alib[::threads]
246
247/// Type alias in namespace \b alib.
249
250} // namespace [alib]
251
252#endif // HPP_ALIB_THREADS_THREADLOCK
Thread * GetOwner() const
bool IsOwnedByCurrentThread() const
ALIB_API void Acquire(const NCString &dbgFile, int dbgLine, const NCString &dbgFunc)
ALIB_API void SetSafeness(lang::Safeness safeness)
lang::Safeness GetSafeness() const
ALIB_API void Release()
defined(ALIB_DOX)
std::condition_variable mutexNotifier
integer DbgWarningAfterWaitTimeInMillis
#define ALIB_API
Definition alib.hpp:538
@ Safe
Do it or treat it with safety.
ALIB_API Thread * getThread(std::thread::id c11ID)
Definition thread.cpp:96
Definition alib.cpp:57
threads::Thread Thread
Type alias in namespace alib.
Definition thread.hpp:390
threads::ThreadLock ThreadLock
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:286