|
|
|||
Warning, file /include/Geant4/PTL/AutoLock.hh was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 // 0002 // MIT License 0003 // Copyright (c) 2020 Jonathan R. Madsen 0004 // Permission is hereby granted, free of charge, to any person obtaining a copy 0005 // of this software and associated documentation files (the "Software"), to deal 0006 // in the Software without restriction, including without limitation the rights 0007 // to use, copy, modify, merge, publish, distribute, sublicense, and 0008 // copies of the Software, and to permit persons to whom the Software is 0009 // furnished to do so, subject to the following conditions: 0010 // The above copyright notice and this permission notice shall be included in 0011 // all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 0012 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 0013 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 0014 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 0015 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 0016 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 0017 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 0018 // 0019 // 0020 // --------------------------------------------------------------- 0021 // Tasking class header file 0022 // 0023 /// Class Description: 0024 /// 0025 /// This class provides a mechanism to create a mutex and locks/unlocks it. 0026 /// Can be used by applications to implement in a portable way a mutexing logic. 0027 /// Usage Example: 0028 /// 0029 /// #include "Threading.hh" 0030 /// #include "AutoLock.hh" 0031 /// 0032 /// /// defined somewhere -- static so all threads see the same mutex 0033 /// static Mutex aMutex; 0034 /// 0035 /// /// somewhere else: 0036 /// /// The AutoLock instance will automatically unlock the mutex when it 0037 /// /// goes out of scope. One typically defines the scope within { } if 0038 /// /// there is thread-safe code following the auto-lock 0039 /// 0040 /// { 0041 /// AutoLock l(&aMutex); 0042 /// ProtectedCode(); 0043 /// } 0044 /// 0045 /// UnprotectedCode(); 0046 /// 0047 /// /// When ProtectedCode() is calling a function that also tries to lock 0048 /// /// a normal AutoLock + Mutex will "deadlock". In other words, the 0049 /// /// the mutex in the ProtectedCode() function will wait forever to 0050 /// /// acquire the lock that is being held by the function that called 0051 /// /// ProtectedCode(). In this situation, use a RecursiveAutoLock + 0052 /// /// RecursiveMutex, e.g. 0053 /// 0054 /// /// defined somewhere -- static so all threads see the same mutex 0055 /// static RecursiveMutex aRecursiveMutex; 0056 /// 0057 /// /// this function is sometimes called directly and sometimes called 0058 /// /// from SomeFunction_B(), which also locks the mutex 0059 /// void SomeFunction_A() 0060 /// { 0061 /// /// when called from SomeFunction_B(), a Mutex + AutoLock will 0062 /// /// deadlock 0063 /// RecursiveAutoLock l(&aRecursiveMutex); 0064 /// /// do something 0065 /// } 0066 /// 0067 /// void SomeFunction_B() 0068 /// { 0069 /// 0070 /// { 0071 /// RecursiveAutoLock l(&aRecursiveMutex); 0072 /// SomeFunction_A(); 0073 /// } 0074 /// 0075 /// UnprotectedCode(); 0076 /// } 0077 /// 0078 /// 0079 /// --------------------------------------------------------------- 0080 /// Author: Andrea Dotti (15 Feb 2013): First Implementation 0081 /// 0082 /// Update: Jonathan Madsen (9 Feb 2018): Replaced custom implementation 0083 /// with inheritance from C++11 unique_lock, which inherits the 0084 /// following member functions: 0085 /// 0086 /// - unique_lock(unique_lock&& other) noexcept; 0087 /// - explicit unique_lock(mutex_type& m); 0088 /// - unique_lock(mutex_type& m, std::defer_lock_t t) noexcept; 0089 /// - unique_lock(mutex_type& m, std::try_to_lock_t t); 0090 /// - unique_lock(mutex_type& m, std::adopt_lock_t t); 0091 /// 0092 /// - template <typename Rep, typename Period> 0093 /// unique_lock(mutex_type& m, 0094 /// const std::chrono::duration<Rep,Period>& 0095 /// timeout_duration); 0096 /// 0097 /// - template<typename Clock, typename Duration> 0098 /// unique_lock(mutex_type& m, 0099 /// const std::chrono::time_point<Clock,Duration>& timeout_time); 0100 /// 0101 /// - void lock(); 0102 /// - void unlock(); 0103 /// - bool try_lock(); 0104 /// 0105 /// - template <typename Rep, typename Period> 0106 /// bool try_lock_for(const std::chrono::duration<Rep,Period>&); 0107 /// 0108 /// - template <typename Rep, typename Period> 0109 /// bool try_lock_until(const std::chrono::time_point<Clock,Duration>&); 0110 /// 0111 /// - void swap(unique_lock& other) noexcept; 0112 /// - mutex_type* release() noexcept; 0113 /// - mutex_type* mutex() const noexcept; 0114 /// - bool owns_lock() const noexcept; 0115 /// - explicit operator bool() const noexcept; 0116 /// - unique_lock& operator=(unique_lock&& other); 0117 /// 0118 /// --------------------------------------------------------------- 0119 /// 0120 /// Note that AutoLock is defined also for a sequential Tasking build but below 0121 /// regarding implementation (also found in Threading.hh) 0122 /// 0123 /// 0124 /// NOTE ON Tasking SERIAL BUILDS AND MUTEX/UNIQUE_LOCK 0125 /// ================================================== 0126 /// 0127 /// Mutex and RecursiveMutex are always C++11 std::mutex types 0128 /// however, in serial mode, using MUTEXLOCK and MUTEXUNLOCK on these 0129 /// types has no effect -- i.e. the mutexes are not actually locked or unlocked 0130 /// 0131 /// Additionally, when a Mutex or RecursiveMutex is used with AutoLock 0132 /// and RecursiveAutoLock, respectively, these classes also suppressing 0133 /// the locking and unlocking of the mutex. Regardless of the build type, 0134 /// AutoLock and RecursiveAutoLock inherit from std::unique_lock<std::mutex> 0135 /// and std::unique_lock<std::recursive_mutex>, respectively. This means 0136 /// that in situations (such as is needed by the analysis category), the 0137 /// AutoLock and RecursiveAutoLock can be passed to functions requesting 0138 /// a std::unique_lock. Within these functions, since std::unique_lock 0139 /// member functions are not virtual, they will not retain the dummy locking 0140 /// and unlocking behavior 0141 /// --> An example of this behavior can be found below 0142 /// 0143 /// Jonathan R. Madsen (February 21, 2018) 0144 /// 0145 /*** 0146 0147 //======================================================================================// 0148 0149 typedef std::unique_lock<std::mutex> unique_lock_t; 0150 // functions for casting AutoLock to std::unique_lock to demonstrate 0151 // that AutoLock is NOT polymorphic 0152 void as_unique_lock(unique_lock_t* lock) { lock->lock(); } 0153 void as_unique_unlock(unique_lock_t* lock) { lock->unlock(); } 0154 0155 //======================================================================================// 0156 0157 void run(const uint64_t& n) 0158 { 0159 // sync the threads a bit 0160 std::this_thread::sleep_for(std::chrono::milliseconds(10)); 0161 0162 // get two mutexes to avoid deadlock when l32 actually locks 0163 AutoLock l32(TypeMutex<int32_t>(), std::defer_lock); 0164 AutoLock l64(TypeMutex<int64_t>(), std::defer_lock); 0165 0166 // when serial: will not execute std::unique_lock::lock() because 0167 // it overrides the member function 0168 l32.lock(); 0169 // regardless of serial or MT: will execute std::unique_lock::lock() 0170 // because std::unique_lock::lock() is not virtual 0171 as_unique_lock(&l64); 0172 0173 std::cout << "Running iteration " << n << "..." << std::endl; 0174 } 0175 0176 //======================================================================================// 0177 // execute some work 0178 template <typename thread_type = std::thread> 0179 void exec(uint64_t n) 0180 { 0181 // get two mutexes to avoid deadlock when l32 actually locks 0182 AutoLock l32(TypeMutex<int32_t>(), std::defer_lock); 0183 AutoLock l64(TypeMutex<int64_t>(), std::defer_lock); 0184 0185 std::vector<thread_type*> threads(n, nullptr); 0186 for(uint64_t i = 0; i < n; ++i) 0187 { 0188 threads[i] = new thread_type(); 0189 *(threads[i]) = std::move(thread_type(run, i)); 0190 } 0191 0192 // when serial: will not execute std::unique_lock::lock() because 0193 // it overrides the member function 0194 l32.lock(); 0195 // regardless of serial or MT: will execute std::unique_lock::lock() 0196 // because std::unique_lock::lock() is not virtual 0197 as_unique_lock(&l64); 0198 0199 std::cout << "Joining..." << std::endl; 0200 0201 // when serial: will not execute std::unique_lock::unlock() because 0202 // it overrides the member function 0203 l32.unlock(); 0204 // regardless of serial or MT: will execute std::unique_lock::unlock() 0205 // because std::unique_lock::unlock() is not virtual 0206 as_unique_unlock(&l64); 0207 0208 // NOTE ABOUT UNLOCKS: 0209 // in MT, commenting out either 0210 // l32.unlock(); 0211 // or 0212 // as_unique_unlock(&l64); 0213 // creates a deadlock; in serial, commenting out 0214 // as_unique_unlock(&l64); 0215 // creates a deadlock but commenting out 0216 // l32.unlock(); 0217 // does not 0218 0219 // clean up and join 0220 for(uint64_t i = 0; i < n; ++i) 0221 { 0222 threads[i]->join(); 0223 delete threads[i]; 0224 } 0225 threads.clear(); 0226 } 0227 0228 //======================================================================================// 0229 0230 int main() 0231 { 0232 print_threading(); 0233 0234 uint64_t n = 30; 0235 std::cout << "\nRunning with real threads...\n" << std::endl; 0236 exec<std::thread>(n); 0237 std::cout << "\nRunning with fake threads...\n" << std::endl; 0238 exec<DummyThread>(n); 0239 0240 } 0241 0242 ***/ 0243 0244 #pragma once 0245 0246 #include "PTL/ConsumeParameters.hh" 0247 #include "PTL/Types.hh" 0248 0249 #include <chrono> 0250 #include <iostream> 0251 #include <mutex> 0252 #include <system_error> 0253 0254 namespace PTL 0255 { 0256 // Note: Note that TemplateAutoLock by itself is not thread-safe and 0257 // cannot be shared among threads due to the locked switch 0258 // 0259 template <typename MutexT> 0260 class TemplateAutoLock : public std::unique_lock<MutexT> 0261 { 0262 public: 0263 //------------------------------------------------------------------------// 0264 // Some useful typedefs 0265 //------------------------------------------------------------------------// 0266 using unique_lock_t = std::unique_lock<MutexT>; 0267 using this_type = TemplateAutoLock<MutexT>; 0268 using mutex_type = typename unique_lock_t::mutex_type; 0269 0270 public: 0271 //------------------------------------------------------------------------// 0272 // STL-consistent reference form constructors 0273 //------------------------------------------------------------------------// 0274 0275 // reference form is consistent with STL lock_guard types 0276 // Locks the associated mutex by calling m.lock(). The behavior is 0277 // undefined if the current thread already owns the mutex except when 0278 // the mutex is recursive 0279 explicit TemplateAutoLock(mutex_type& _mutex) 0280 : unique_lock_t(_mutex, std::defer_lock) 0281 { 0282 // call termination-safe locking. if serial, this call has no effect 0283 _lock_deferred(); 0284 } 0285 0286 // Tries to lock the associated mutex by calling 0287 // m.try_lock_for(_timeout_duration). Blocks until specified 0288 // _timeout_duration has elapsed or the lock is acquired, whichever comes 0289 // first. May block for longer than _timeout_duration. 0290 template <typename Rep, typename Period> 0291 TemplateAutoLock(mutex_type& _mutex, 0292 const std::chrono::duration<Rep, Period>& _timeout_duration) 0293 : unique_lock_t(_mutex, std::defer_lock) 0294 { 0295 // call termination-safe locking. if serial, this call has no effect 0296 _lock_deferred(_timeout_duration); 0297 } 0298 0299 // Tries to lock the associated mutex by calling 0300 // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has 0301 // been reached or the lock is acquired, whichever comes first. May block 0302 // for longer than until _timeout_time has been reached. 0303 template <typename Clock, typename Duration> 0304 TemplateAutoLock(mutex_type& _mutex, 0305 const std::chrono::time_point<Clock, Duration>& _timeout_time) 0306 : unique_lock_t(_mutex, std::defer_lock) 0307 { 0308 // call termination-safe locking. if serial, this call has no effect 0309 _lock_deferred(_timeout_time); 0310 } 0311 0312 // Does not lock the associated mutex. 0313 TemplateAutoLock(mutex_type& _mutex, std::defer_lock_t _lock) noexcept 0314 : unique_lock_t(_mutex, _lock) 0315 {} 0316 0317 // Tries to lock the associated mutex without blocking by calling 0318 // m.try_lock(). The behavior is undefined if the current thread already 0319 // owns the mutex except when the mutex is recursive. 0320 TemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t _lock) 0321 : unique_lock_t(_mutex, _lock) 0322 {} 0323 0324 // Assumes the calling thread already owns m 0325 TemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t _lock) 0326 : unique_lock_t(_mutex, _lock) 0327 {} 0328 0329 public: 0330 //------------------------------------------------------------------------// 0331 // Backwards compatibility versions (constructor with pointer to mutex) 0332 //------------------------------------------------------------------------// 0333 TemplateAutoLock(mutex_type* _mutex) 0334 : unique_lock_t(*_mutex, std::defer_lock) 0335 { 0336 // call termination-safe locking. if serial, this call has no effect 0337 _lock_deferred(); 0338 } 0339 0340 TemplateAutoLock(mutex_type* _mutex, std::defer_lock_t _lock) noexcept 0341 : unique_lock_t(*_mutex, _lock) 0342 {} 0343 0344 TemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t _lock) 0345 : unique_lock_t(*_mutex, _lock) 0346 {} 0347 0348 TemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t _lock) 0349 : unique_lock_t(*_mutex, _lock) 0350 {} 0351 0352 private: 0353 // helpful macros 0354 #define _is_stand_mutex(Tp) (std::is_same<Tp, Mutex>::value) 0355 #define _is_recur_mutex(Tp) (std::is_same<Tp, RecursiveMutex>::value) 0356 #define _is_other_mutex(Tp) (!_is_stand_mutex(Tp) && !_is_recur_mutex(Tp)) 0357 0358 template <typename Tp = MutexT, 0359 typename std::enable_if<_is_stand_mutex(Tp), int>::type = 0> 0360 std::string GetTypeString() 0361 { 0362 return "AutoLock<Mutex>"; 0363 } 0364 0365 template <typename Tp = MutexT, 0366 typename std::enable_if<_is_recur_mutex(Tp), int>::type = 0> 0367 std::string GetTypeString() 0368 { 0369 return "AutoLock<RecursiveMutex>"; 0370 } 0371 0372 template <typename Tp = MutexT, 0373 typename std::enable_if<_is_other_mutex(Tp), int>::type = 0> 0374 std::string GetTypeString() 0375 { 0376 return "AutoLock<UNKNOWN_MUTEX>"; 0377 } 0378 0379 // pollution is bad 0380 #undef _is_stand_mutex 0381 #undef _is_recur_mutex 0382 #undef _is_other_mutex 0383 0384 //========================================================================// 0385 // NOTE on _lock_deferred(...) variants: 0386 // a system_error in lock means that the mutex is unavailable 0387 // we want to throw the error that comes from locking an unavailable 0388 // mutex so that we know there is a memory leak 0389 // if the mutex is valid, this will hold until the other thread 0390 // finishes 0391 0392 // sometimes certain destructors use locks, this isn't an issue unless 0393 // the object is leaked. When this occurs, the application finalization 0394 // (i.e. the real or implied "return 0" part of main) will call destructors 0395 // on Tasking object after some static mutex variables are deleted, leading 0396 // to the error code (typically on Clang compilers): 0397 // libc++abi.dylib: terminating with uncaught exception of type 0398 // std::__1::system_error: mutex lock failed: Invalid argument 0399 // this function protects against this failure until such a time that 0400 // these issues have been resolved 0401 0402 //========================================================================// 0403 // standard locking 0404 inline void _lock_deferred() 0405 { 0406 try 0407 { 0408 this->unique_lock_t::lock(); 0409 } catch(std::system_error& e) 0410 { 0411 PrintLockErrorMessage(e); 0412 } 0413 } 0414 0415 //========================================================================// 0416 // Tries to lock the associated mutex by calling 0417 // m.try_lock_for(_timeout_duration). Blocks until specified 0418 // _timeout_duration has elapsed or the lock is acquired, whichever comes 0419 // first. May block for longer than _timeout_duration. 0420 template <typename Rep, typename Period> 0421 void _lock_deferred(const std::chrono::duration<Rep, Period>& _timeout_duration) 0422 { 0423 try 0424 { 0425 this->unique_lock_t::try_lock_for(_timeout_duration); 0426 } catch(std::system_error& e) 0427 { 0428 PrintLockErrorMessage(e); 0429 } 0430 } 0431 0432 //========================================================================// 0433 // Tries to lock the associated mutex by calling 0434 // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has 0435 // been reached or the lock is acquired, whichever comes first. May block 0436 // for longer than until _timeout_time has been reached. 0437 template <typename Clock, typename Duration> 0438 void _lock_deferred(const std::chrono::time_point<Clock, Duration>& _timeout_time) 0439 { 0440 try 0441 { 0442 this->unique_lock_t::try_lock_until(_timeout_time); 0443 } catch(std::system_error& e) 0444 { 0445 PrintLockErrorMessage(e); 0446 } 0447 } 0448 0449 //========================================================================// 0450 // the message for what mutex lock fails due to deleted static mutex 0451 // at termination 0452 void PrintLockErrorMessage(std::system_error& e) 0453 { 0454 // use std::cout/std::endl to avoid include dependencies 0455 using std::cout; 0456 using std::endl; 0457 // the error that comes from locking an unavailable mutex 0458 #if defined(VERBOSE) 0459 cout << "Non-critical error: mutex lock failure in " 0460 << GetTypeString<mutex_type>() << ". " 0461 << "If the app is terminating, Tasking failed to " 0462 << "delete an allocated resource and a Tasking destructor is " 0463 << "being called after the statics were destroyed. \n\t--> " 0464 << "Exception: [code: " << e.code() << "] caught: " << e.what() << std::endl; 0465 #else 0466 ConsumeParameters(e); 0467 #endif 0468 } 0469 }; 0470 0471 // -------------------------------------------------------------------------- // 0472 // 0473 // Use the non-template types below: 0474 // - AutoLock with Mutex 0475 // - RecursiveAutoLock with RecursiveMutex 0476 // 0477 // -------------------------------------------------------------------------- // 0478 0479 using AutoLock = TemplateAutoLock<Mutex>; 0480 using RecursiveAutoLock = TemplateAutoLock<RecursiveMutex>; 0481 0482 } // namespace PTL
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|