ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
threadlock.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !defined(ALIB_DOX)
10#if !defined (HPP_ALIB_THREADS_THREADLOCK)
12#endif
13
14#if !defined (HPP_ALIB_THREADS_DETAIL_THREADMAP)
16#endif
17
18#if !defined (HPP_ALIB_STRINGS_LOCALSTRING)
20#endif
21#endif // !defined(ALIB_DOX)
22
23namespace alib { namespace threads {
24
26: owner(std::thread::id())
27, safeness(pSafeness)
28{}
29
31{
32 ALIB_ASSERT_WARNING( cntAcquirements == 0, "THREADS", "Lcok destruction while locked" )
33}
34
35
36#if !defined(ALIB_DOX)
37
40void ThreadLock::Acquire( const NCString& dbgFile, int dbgLine, const NCString& dbgFunc ) )
41{
42 // are we in unsafe mode?
43 if ( GetSafeness() == lang::Safeness::Unsafe )
44 {
45 // we are still increasing the cntAcquirements
46 ++cntAcquirements;
47
48 // reached warning limit?
49 ALIB_ASSERT_WARNING( cntAcquirements != 0
50 && DbgRecursionWarningThreshold != 0
51 && (cntAcquirements % DbgRecursionWarningThreshold) != 0,
52 "THREADS", "Recursion depth warning.\n"
53 "To prevent this, change ThreadLock.recursionWarningThreshold or fix your code.\n"
54 "Depth: ", cntAcquirements )
55
56 // end of unsafe version of this method
57 return;
58 }
59
60 auto thisThreadID= std::this_thread::get_id();
61
62 // this thread already owns the lock
63 if( thisThreadID == owner )
64 {
65 // we are still increasing the cntAcquirements
66 ++cntAcquirements;
67
68 // reached warning limit?
69 ALIB_ASSERT_WARNING( cntAcquirements != 0
70 && DbgRecursionWarningThreshold != 0
71 && (cntAcquirements % DbgRecursionWarningThreshold) != 0,
72 "THREADS", "Recursion depth warning.\n"
73 "To prevent this, change ThreadLock.recursionWarningThreshold or fix your code.\n"
74 "Depth: ", cntAcquirements )
75
76 // that's it
77 return;
78 }
79
80 #if ALIB_DEBUG
81 bool hasWarned= false;
82 std::chrono::steady_clock::time_point waitTime= std::chrono::steady_clock::now();
83 #endif
84
85 // synchronize on mutex
86 {std::unique_lock<std::mutex> lock(mutex);
87
88 // if we do not own this object, wait until it is set free by somebody else
89 while( owner != std::thread::id() ) // needs a loop due to spurious wakeups
90 {
91 // wait unconditional
92 #if ALIB_DEBUG
93 if( DbgWarningAfterWaitTimeInMillis <= 0 || hasWarned )
94 #endif
95
96 mutexNotifier.wait( lock ); // releases the lock and waits on the notifier
97
98
99 #if ALIB_DEBUG
100 // wait with time limit
101 else
102 {
103 mutexNotifier.wait_for( lock, std::chrono::milliseconds(DbgWarningAfterWaitTimeInMillis) );
104
105 auto milliseconds= std::chrono::duration_cast<std::chrono::milliseconds>(
106 std::chrono::steady_clock::now() - waitTime ).count();
107
108 if ( milliseconds >= DbgWarningAfterWaitTimeInMillis )
109 {
110 // release the lock to allow the warning. But copy the owner's id before.
111 auto actualOwner= owner;
112 lock.unlock();
113
114 hasWarned= true;
115 Thread* thisThread = Thread::GetCurrent();
116 Thread* ownerThread= detail::getThread(actualOwner);
117 ALIB_WARNING( "THREADS", NString1K()
118 << "Waiting on ThreadLock since " << milliseconds << " ms. Reasons might be "
119 "a dead lock, an non-optimized critical section\n"
120 "or simply too much load on the executing machine. More Info:"
121 << "\n Owner : " << ownerThread->GetId() << '/' << ownerThread->GetName()
122 << " at: " << DbgOwnerFile << ':' << DbgOwnerLine << " " << DbgOwnerFunc << "()."
123 << "\n This thread : " << thisThread ->GetId() << '/' << thisThread ->GetName()
124 << " at: " << dbgFile << ':' << dbgLine << " " << dbgFunc << "()."
125 )
126 lock.lock();
127 }
128 }
129 #endif
130
131 } // while loop
132
133 // take control
134 owner= thisThreadID;
135 cntAcquirements= 1;
136 #if ALIB_DEBUG
137 DbgOwnerFile= dbgFile;
138 DbgOwnerLine= dbgLine;
139 DbgOwnerFunc= dbgFunc;
140 #endif
141
142 }
143}
144
145#endif //!defined(ALIB_DOX)
146
148{
149 // are we in unsafe mode?
151 {
152 // not locked
153 if( cntAcquirements == 0 )
154 {
155 ALIB_ERROR( "THREADS", "Release without acquire (unsafe mode)."
156 "Note: This must never happen, check your code, set lock to safe mode!" )
157 }
158
159 // we are still decreasing the cntAcquirements
161
162 // end of unsafe version of this method
163 return;
164 }
165
166 // synchronize on mutex
167 {
168 std::unique_lock<std::mutex> lock(mutex);
169
170 // not locked?
171 ALIB_ASSERT_ERROR( cntAcquirements != 0, "THREADS", "Illegal release without acquire (safe mode)." )
172
173 // decreasing the cntAcquirements
175
176 // release and notify next waiting thread
177 if( cntAcquirements == 0 )
178 {
179 owner= std::thread::id();
180 mutexNotifier.notify_one();
181 }
182 } // synchronized
183}
184
185
187{
188 // are we in unsafe mode?
190 {
191 // already locked? ALIB Error
192 if( cntAcquirements != 0 )
193 {
194 ALIB_ERROR( "THREADS", "Cannot switch safeness mode while already locked.\n"
195 " Current mode: unsafe, requested mode: ",
196 (newSafeness == lang::Safeness::Safe ? "Safe" : "Unsafe" )
197 )
198
199 return;
200 }
201
202 safeness= newSafeness;
203 return;
204 }
205
206 // we are currently in safe mode: synchronize on mutex
207 {
208 std::unique_lock<std::mutex> lock(mutex);
209
210 // already locked? Assertion
211 if ( owner != std::thread::id() )
212 {
213 lock.unlock();
214 ALIB_DBG(Thread* ownerThread= GetOwner() );
215 ALIB_ERROR( "THREADS", NString256() <<
216 "Cannot switch safeness mode while already locked.\n"
217 " Current mode: safe, requested mode: " <<
218 (safeness == lang::Safeness::Safe ? "Safe" : "Unsafe" )
219 << "\n"
220 " Owner: " << ownerThread->GetId() << '/' << ownerThread->GetName() )
221 return;
222 }
223
224 // switch off?
225 safeness= newSafeness;
226 }
227}
228
229
230}} // namespace [alib::threads]
Thread * GetOwner() const
ALIB_API void Acquire(const NCString &dbgFile, int dbgLine, const NCString &dbgFunc)
ALIB_API ThreadLock(lang::Safeness safeness=lang::Safeness::Safe)
ALIB_API void SetSafeness(lang::Safeness safeness)
lang::Safeness GetSafeness() const
ALIB_API void Release()
defined(ALIB_DOX)
std::condition_variable mutexNotifier
static Thread * GetCurrent()
Definition thread.hpp:288
#define ALIB_WARNING(...)
Definition alib.hpp:981
#define ALIB_ERROR(...)
Definition alib.hpp:980
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:984
#define ALIB_ASSERT_WARNING(cond,...)
Definition alib.hpp:985
#define ALIB_DBG(...)
Definition alib.hpp:457
#define ALIB_REL_DBG(releaseCode,...)
Definition alib.hpp:459
@ Safe
Do it or treat it with safety.
@ Unsafe
Omit checks or perform unsafe operations.
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
NLocalString< 256 > NString256
Type alias name for TLocalString<nchar,256> .
NLocalString< 1024 > NString1K
Type alias name for TLocalString<nchar,1024> .