|
||||
File indexing completed on 2025-01-18 09:57:56
0001 // 0002 // ******************************************************************** 0003 // * License and Disclaimer * 0004 // * * 0005 // * The Geant4 software is copyright of the Copyright Holders of * 0006 // * the Geant4 Collaboration. It is provided under the terms and * 0007 // * conditions of the Geant4 Software License, included in the file * 0008 // * LICENSE and available at http://cern.ch/geant4/license . These * 0009 // * include a list of copyright holders. * 0010 // * * 0011 // * Neither the authors of this software system, nor their employing * 0012 // * institutes,nor the agencies providing financial support for this * 0013 // * work make any representation or warranty, express or implied, * 0014 // * regarding this software system or assume any liability for its * 0015 // * use. Please see the license in the file LICENSE and URL above * 0016 // * for the full disclaimer and the limitation of liability. * 0017 // * * 0018 // * This code implementation is the result of the scientific and * 0019 // * technical work of the GEANT4 collaboration. * 0020 // * By using, copying, modifying or distributing the software (or * 0021 // * any work based on the software) you agree to acknowledge its * 0022 // * use in resulting scientific publications, and indicate your * 0023 // * acceptance of all terms of the Geant4 Software license. * 0024 // ******************************************************************** 0025 // 0026 // G4Autolock 0027 // 0028 // Class Description: 0029 // 0030 // This class provides a mechanism to create a mutex and locks/unlocks it. 0031 // Can be used by applications to implement in a portable way a mutexing logic. 0032 // Usage Example: 0033 // 0034 // #include "G4Threading.hh" 0035 // #include "G4AutoLock.hh" 0036 // 0037 // // defined somewhere -- static so all threads see the same mutex 0038 // static G4Mutex aMutex; 0039 // 0040 // // somewhere else: 0041 // // The G4AutoLock instance will automatically unlock the mutex when it 0042 // // goes out of scope. One typically defines the scope within { } if 0043 // // there is thread-safe code following the auto-lock 0044 // 0045 // { 0046 // G4AutoLock l(&aMutex); 0047 // ProtectedCode(); 0048 // } 0049 // 0050 // UnprotectedCode(); 0051 // 0052 // // When ProtectedCode() is calling a function that also tries to lock 0053 // // a normal G4AutoLock + G4Mutex will "deadlock". In other words, the 0054 // // the mutex in the ProtectedCode() function will wait forever to 0055 // // acquire the lock that is being held by the function that called 0056 // // ProtectedCode(). In this situation, use a G4RecursiveAutoLock + 0057 // // G4RecursiveMutex, e.g. 0058 // 0059 // // defined somewhere -- static so all threads see the same mutex 0060 // static G4RecursiveMutex aRecursiveMutex; 0061 // 0062 // // this function is sometimes called directly and sometimes called 0063 // // from SomeFunction_B(), which also locks the mutex 0064 // void SomeFunction_A() 0065 // { 0066 // // when called from SomeFunction_B(), a G4Mutex + G4AutoLock will 0067 // // deadlock 0068 // G4RecursiveAutoLock l(&aRecursiveMutex); 0069 // // do something 0070 // } 0071 // 0072 // void SomeFunction_B() 0073 // { 0074 // 0075 // { 0076 // G4RecursiveAutoLock l(&aRecursiveMutex); 0077 // SomeFunction_A(); 0078 // } 0079 // 0080 // UnprotectedCode(); 0081 // } 0082 // 0083 0084 // -------------------------------------------------------------------- 0085 // Author: Andrea Dotti (15 Feb 2013): First Implementation 0086 // 0087 // Update: Jonathan Madsen (9 Feb 2018): Replaced custom implementation 0088 // with inheritance from C++11 unique_lock, which inherits the 0089 // following member functions: 0090 // 0091 // - unique_lock(unique_lock&& other) noexcept; 0092 // - explicit unique_lock(mutex_type& m); 0093 // - unique_lock(mutex_type& m, std::defer_lock_t t) noexcept; 0094 // - unique_lock(mutex_type& m, std::try_to_lock_t t); 0095 // - unique_lock(mutex_type& m, std::adopt_lock_t t); 0096 // 0097 // - template <typename Rep, typename Period> 0098 // unique_lock(mutex_type& m, 0099 // const std::chrono::duration<Rep,Period>& timeout_duration); 0100 // 0101 // - template<typename Clock, typename Duration> 0102 // unique_lock(mutex_type& m, 0103 // const std::chrono::time_point<Clock,Duration>& timeout_time); 0104 // 0105 // - void lock(); 0106 // - void unlock(); 0107 // - bool try_lock(); 0108 // 0109 // - template <typename Rep, typename Period> 0110 // bool try_lock_for(const std::chrono::duration<Rep,Period>&); 0111 // 0112 // - template <typename Rep, typename Period> 0113 // bool try_lock_until(const std::chrono::time_point<Clock,Duration>&); 0114 // 0115 // - void swap(unique_lock& other) noexcept; 0116 // - mutex_type* release() noexcept; 0117 // - mutex_type* mutex() const noexcept; 0118 // - bool owns_lock() const noexcept; 0119 // - explicit operator bool() const noexcept; 0120 // - unique_lock& operator=(unique_lock&& other); 0121 // 0122 // -------------------------------------------------------------------- 0123 // 0124 // Note that G4AutoLock is defined also for a sequential Geant4 build but below 0125 // regarding implementation (also found in G4Threading.hh) 0126 // 0127 // 0128 // NOTE ON GEANT4 SERIAL BUILDS AND MUTEX/UNIQUE_LOCK 0129 // ================================================== 0130 // 0131 // G4Mutex and G4RecursiveMutex are always C++11 std::mutex types 0132 // however, in serial mode, using G4MUTEXLOCK and G4MUTEXUNLOCK on these 0133 // types has no effect -- i.e. the mutexes are not actually locked or unlocked 0134 // 0135 // Additionally, when a G4Mutex or G4RecursiveMutex is used with G4AutoLock 0136 // and G4RecursiveAutoLock, respectively, these classes also suppressing 0137 // the locking and unlocking of the mutex. Regardless of the build type, 0138 // G4AutoLock and G4RecursiveAutoLock inherit from std::unique_lock<std::mutex> 0139 // and std::unique_lock<std::recursive_mutex>, respectively. This means 0140 // that in situations (such as is needed by the analysis category), the 0141 // G4AutoLock and G4RecursiveAutoLock can be passed to functions requesting 0142 // a std::unique_lock. Within these functions, since std::unique_lock 0143 // member functions are not virtual, they will not retain the dummy locking 0144 // and unlocking behavior 0145 // --> An example of this behavior can be found below 0146 // 0147 // Jonathan R. Madsen (February 21, 2018) 0148 // 0149 /** 0150 0151 //============================================================================// 0152 0153 void print_threading() 0154 { 0155 #ifdef G4MULTITHREADED 0156 std::cout << "\nUsing G4MULTITHREADED version..." << std::endl; 0157 #else 0158 std::cout << "\nUsing G4SERIAL version..." << std::endl; 0159 #endif 0160 } 0161 0162 //============================================================================// 0163 0164 typedef std::unique_lock<std::mutex> unique_lock_t; 0165 // functions for casting G4AutoLock to std::unique_lock to demonstrate 0166 // that G4AutoLock is NOT polymorphic 0167 void as_unique_lock(unique_lock_t* lock) { lock->lock(); } 0168 void as_unique_unlock(unique_lock_t* lock) { lock->unlock(); } 0169 0170 //============================================================================// 0171 0172 void run(const uint64_t& n) 0173 { 0174 // sync the threads a bit 0175 std::this_thread::sleep_for(std::chrono::milliseconds(10)); 0176 0177 // get two mutexes to avoid deadlock when l32 actually locks 0178 G4AutoLock l32(G4TypeMutex<int32_t>(), std::defer_lock); 0179 G4AutoLock l64(G4TypeMutex<int64_t>(), std::defer_lock); 0180 0181 // when serial: will not execute std::unique_lock::lock() because 0182 // it overrides the member function 0183 l32.lock(); 0184 // regardless of serial or MT: will execute std::unique_lock::lock() 0185 // because std::unique_lock::lock() is not virtual 0186 as_unique_lock(&l64); 0187 0188 std::cout << "Running iteration " << n << "..." << std::endl; 0189 } 0190 0191 //============================================================================// 0192 // execute some work 0193 template <typename thread_type = std::thread> 0194 void exec(uint64_t n) 0195 { 0196 // get two mutexes to avoid deadlock when l32 actually locks 0197 G4AutoLock l32(G4TypeMutex<int32_t>(), std::defer_lock); 0198 G4AutoLock l64(G4TypeMutex<int64_t>(), std::defer_lock); 0199 0200 std::vector<thread_type*> threads(n, nullptr); 0201 for(uint64_t i = 0; i < n; ++i) 0202 { 0203 threads[i] = new thread_type(); 0204 *(threads[i]) = std::move(thread_type(run, i)); 0205 } 0206 0207 // when serial: will not execute std::unique_lock::lock() because 0208 // it overrides the member function 0209 l32.lock(); 0210 // regardless of serial or MT: will execute std::unique_lock::lock() 0211 // because std::unique_lock::lock() is not virtual 0212 as_unique_lock(&l64); 0213 0214 std::cout << "Joining..." << std::endl; 0215 0216 // when serial: will not execute std::unique_lock::unlock() because 0217 // it overrides the member function 0218 l32.unlock(); 0219 // regardless of serial or MT: will execute std::unique_lock::unlock() 0220 // because std::unique_lock::unlock() is not virtual 0221 as_unique_unlock(&l64); 0222 0223 // NOTE ABOUT UNLOCKS: 0224 // in MT, commenting out either 0225 // l32.unlock(); 0226 // or 0227 // as_unique_unlock(&l64); 0228 // creates a deadlock; in serial, commenting out 0229 // as_unique_unlock(&l64); 0230 // creates a deadlock but commenting out 0231 // l32.unlock(); 0232 // does not 0233 0234 // clean up and join 0235 for(uint64_t i = 0; i < n; ++i) 0236 { 0237 threads[i]->join(); 0238 delete threads[i]; 0239 } 0240 threads.clear(); 0241 } 0242 0243 //============================================================================// 0244 0245 int main() 0246 { 0247 print_threading(); 0248 0249 uint64_t n = 30; 0250 std::cout << "\nRunning with real threads...\n" << std::endl; 0251 exec<std::thread>(n); 0252 std::cout << "\nRunning with fake threads...\n" << std::endl; 0253 exec<G4DummyThread>(n); 0254 0255 } 0256 0257 **/ 0258 // -------------------------------------------------------------------- 0259 #ifndef G4AUTOLOCK_HH 0260 #define G4AUTOLOCK_HH 0261 0262 #include "G4Threading.hh" 0263 0264 #include <chrono> 0265 #include <iostream> 0266 #include <mutex> 0267 #include <system_error> 0268 0269 // Note: Note that G4TemplateAutoLock by itself is not thread-safe and 0270 // cannot be shared among threads due to the locked switch 0271 // 0272 template <typename _Mutex_t> 0273 class G4TemplateAutoLock : public std::unique_lock<_Mutex_t> 0274 { 0275 public: 0276 //------------------------------------------------------------------------// 0277 // Some useful typedefs 0278 //------------------------------------------------------------------------// 0279 using unique_lock_t = std::unique_lock<_Mutex_t>; 0280 using this_type = G4TemplateAutoLock<_Mutex_t>; 0281 using mutex_type = typename unique_lock_t::mutex_type; 0282 0283 public: 0284 //------------------------------------------------------------------------// 0285 // STL-consistent reference form constructors 0286 //------------------------------------------------------------------------// 0287 0288 // reference form is consistent with STL lock_guard types 0289 // Locks the associated mutex by calling m.lock(). The behavior is 0290 // undefined if the current thread already owns the mutex except when 0291 // the mutex is recursive 0292 G4TemplateAutoLock(mutex_type& _mutex) 0293 : unique_lock_t(_mutex, std::defer_lock) 0294 { 0295 // call termination-safe locking. if serial, this call has no effect 0296 _lock_deferred(); 0297 } 0298 0299 // Tries to lock the associated mutex by calling 0300 // m.try_lock_for(_timeout_duration). Blocks until specified 0301 // _timeout_duration has elapsed or the lock is acquired, whichever comes 0302 // first. May block for longer than _timeout_duration. 0303 template <typename Rep, typename Period> 0304 G4TemplateAutoLock( 0305 mutex_type& _mutex, 0306 const std::chrono::duration<Rep, Period>& _timeout_duration) 0307 : unique_lock_t(_mutex, std::defer_lock) 0308 { 0309 // call termination-safe locking. if serial, this call has no effect 0310 _lock_deferred(_timeout_duration); 0311 } 0312 0313 // Tries to lock the associated mutex by calling 0314 // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has 0315 // been reached or the lock is acquired, whichever comes first. May block 0316 // for longer than until _timeout_time has been reached. 0317 template <typename Clock, typename Duration> 0318 G4TemplateAutoLock( 0319 mutex_type& _mutex, 0320 const std::chrono::time_point<Clock, Duration>& _timeout_time) 0321 : unique_lock_t(_mutex, std::defer_lock) 0322 { 0323 // call termination-safe locking. if serial, this call has no effect 0324 _lock_deferred(_timeout_time); 0325 } 0326 0327 // Does not lock the associated mutex. 0328 G4TemplateAutoLock(mutex_type& _mutex, std::defer_lock_t _lock) noexcept 0329 : unique_lock_t(_mutex, _lock) 0330 {} 0331 0332 #ifdef G4MULTITHREADED 0333 0334 // Tries to lock the associated mutex without blocking by calling 0335 // m.try_lock(). The behavior is undefined if the current thread already 0336 // owns the mutex except when the mutex is recursive. 0337 G4TemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t _lock) 0338 : unique_lock_t(_mutex, _lock) 0339 {} 0340 0341 // Assumes the calling thread already owns m 0342 G4TemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t _lock) 0343 : unique_lock_t(_mutex, _lock) 0344 {} 0345 0346 #else 0347 0348 // serial dummy version (initializes unique_lock but does not lock) 0349 G4TemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t) 0350 : unique_lock_t(_mutex, std::defer_lock) 0351 {} 0352 0353 // serial dummy version (initializes unique_lock but does not lock) 0354 G4TemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t) 0355 : unique_lock_t(_mutex, std::defer_lock) 0356 {} 0357 0358 #endif // defined(G4MULTITHREADED) 0359 0360 public: 0361 //------------------------------------------------------------------------// 0362 // Backwards compatibility versions (constructor with pointer to mutex) 0363 //------------------------------------------------------------------------// 0364 G4TemplateAutoLock(mutex_type* _mutex) 0365 : unique_lock_t(*_mutex, std::defer_lock) 0366 { 0367 // call termination-safe locking. if serial, this call has no effect 0368 _lock_deferred(); 0369 } 0370 0371 G4TemplateAutoLock(mutex_type* _mutex, std::defer_lock_t _lock) noexcept 0372 : unique_lock_t(*_mutex, _lock) 0373 {} 0374 0375 #if defined(G4MULTITHREADED) 0376 0377 G4TemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t _lock) 0378 : unique_lock_t(*_mutex, _lock) 0379 {} 0380 0381 G4TemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t _lock) 0382 : unique_lock_t(*_mutex, _lock) 0383 {} 0384 0385 #else // NOT defined(G4MULTITHREADED) -- i.e. serial 0386 0387 G4TemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t) 0388 : unique_lock_t(*_mutex, std::defer_lock) 0389 {} 0390 0391 G4TemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t) 0392 : unique_lock_t(*_mutex, std::defer_lock) 0393 {} 0394 0395 #endif // defined(G4MULTITHREADED) 0396 0397 public: 0398 //------------------------------------------------------------------------// 0399 // Non-constructor overloads 0400 //------------------------------------------------------------------------// 0401 0402 #if defined(G4MULTITHREADED) 0403 0404 // overload nothing 0405 0406 #else // NOT defined(G4MULTITHREADED) -- i.e. serial 0407 0408 // override unique lock member functions to keep from locking/unlocking 0409 // but does not override in polymorphic usage 0410 void lock() {} 0411 void unlock() {} 0412 bool try_lock() { return true; } 0413 0414 template <typename Rep, typename Period> 0415 bool try_lock_for(const std::chrono::duration<Rep, Period>&) 0416 { 0417 return true; 0418 } 0419 0420 template <typename Clock, typename Duration> 0421 bool try_lock_until(const std::chrono::time_point<Clock, Duration>&) 0422 { 0423 return true; 0424 } 0425 0426 void swap(this_type& other) noexcept { std::swap(*this, other); } 0427 bool owns_lock() const noexcept { return false; } 0428 0429 // no need to overload 0430 // explicit operator bool() const noexcept; 0431 // this_type& operator=(this_type&& other); 0432 // mutex_type* release() noexcept; 0433 // mutex_type* mutex() const noexcept; 0434 0435 #endif // defined(G4MULTITHREADED) 0436 0437 private: 0438 // helpful macros 0439 #define _is_stand_mutex(_Tp) (std::is_same<_Tp, G4Mutex>::value) 0440 #define _is_recur_mutex(_Tp) (std::is_same<_Tp, G4RecursiveMutex>::value) 0441 #define _is_other_mutex(_Tp) (!_is_stand_mutex(_Tp) && !_is_recur_mutex(_Tp)) 0442 0443 template <typename _Tp = _Mutex_t, 0444 typename std::enable_if<_is_stand_mutex(_Tp), int>::type = 0> 0445 std::string GetTypeString() 0446 { 0447 return "G4AutoLock<G4Mutex>"; 0448 } 0449 0450 template <typename _Tp = _Mutex_t, 0451 typename std::enable_if<_is_recur_mutex(_Tp), int>::type = 0> 0452 std::string GetTypeString() 0453 { 0454 return "G4AutoLock<G4RecursiveMutex>"; 0455 } 0456 0457 template <typename _Tp = _Mutex_t, 0458 typename std::enable_if<_is_other_mutex(_Tp), int>::type = 0> 0459 std::string GetTypeString() 0460 { 0461 return "G4AutoLock<UNKNOWN_MUTEX>"; 0462 } 0463 0464 // pollution is bad 0465 #undef _is_stand_mutex 0466 #undef _is_recur_mutex 0467 #undef _is_other_mutex 0468 0469 // used in _lock_deferred chrono variants to avoid ununsed-variable warning 0470 template <typename _Tp> 0471 void suppress_unused_variable(const _Tp&) 0472 {} 0473 0474 //========================================================================// 0475 // NOTE on _lock_deferred(...) variants: 0476 // a system_error in lock means that the mutex is unavailable 0477 // we want to throw the error that comes from locking an unavailable 0478 // mutex so that we know there is a memory leak 0479 // if the mutex is valid, this will hold until the other thread 0480 // finishes 0481 0482 // sometimes certain destructors use locks, this isn't an issue unless 0483 // the object is leaked. When this occurs, the application finalization 0484 // (i.e. the real or implied "return 0" part of main) will call destructors 0485 // on Geant4 object after some static mutex variables are deleted, leading 0486 // to the error code (typically on Clang compilers): 0487 // libc++abi.dylib: terminating with uncaught exception of type 0488 // std::__1::system_error: mutex lock failed: Invalid argument 0489 // this function protects against this failure until such a time that 0490 // these issues have been resolved 0491 0492 //========================================================================// 0493 // standard locking 0494 inline void _lock_deferred() 0495 { 0496 #if defined(G4MULTITHREADED) 0497 try 0498 { 0499 this->unique_lock_t::lock(); 0500 } catch(std::system_error& e) 0501 { 0502 PrintLockErrorMessage(e); 0503 } 0504 #endif 0505 } 0506 0507 //========================================================================// 0508 // Tries to lock the associated mutex by calling 0509 // m.try_lock_for(_timeout_duration). Blocks until specified 0510 // _timeout_duration has elapsed or the lock is acquired, whichever comes 0511 // first. May block for longer than _timeout_duration. 0512 template <typename Rep, typename Period> 0513 void _lock_deferred( 0514 const std::chrono::duration<Rep, Period>& _timeout_duration) 0515 { 0516 #if defined(G4MULTITHREADED) 0517 try 0518 { 0519 this->unique_lock_t::try_lock_for(_timeout_duration); 0520 } catch(std::system_error& e) 0521 { 0522 PrintLockErrorMessage(e); 0523 } 0524 #else 0525 suppress_unused_variable(_timeout_duration); 0526 #endif 0527 } 0528 0529 //========================================================================// 0530 // Tries to lock the associated mutex by calling 0531 // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has 0532 // been reached or the lock is acquired, whichever comes first. May block 0533 // for longer than until _timeout_time has been reached. 0534 template <typename Clock, typename Duration> 0535 void _lock_deferred( 0536 const std::chrono::time_point<Clock, Duration>& _timeout_time) 0537 { 0538 #if defined(G4MULTITHREADED) 0539 try 0540 { 0541 this->unique_lock_t::try_lock_until(_timeout_time); 0542 } catch(std::system_error& e) 0543 { 0544 PrintLockErrorMessage(e); 0545 } 0546 #else 0547 suppress_unused_variable(_timeout_time); 0548 #endif 0549 } 0550 0551 //========================================================================// 0552 // the message for what mutex lock fails due to deleted static mutex 0553 // at termination 0554 void PrintLockErrorMessage(std::system_error& e) 0555 { 0556 // use std::cout/std::endl to avoid include dependencies 0557 using std::cout; 0558 using std::endl; 0559 // the error that comes from locking an unavailable mutex 0560 #if defined(G4VERBOSE) 0561 cout << "Non-critical error: mutex lock failure in " 0562 << GetTypeString<mutex_type>() << ". " 0563 << "If the app is terminating, Geant4 failed to " 0564 << "delete an allocated resource and a Geant4 destructor is " 0565 << "being called after the statics were destroyed. \n\t--> " 0566 << "Exception: [code: " << e.code() << "] caught: " << e.what() 0567 << endl; 0568 #else 0569 suppress_unused_variable(e); 0570 #endif 0571 } 0572 }; 0573 0574 // -------------------------------------------------------------------------- // 0575 // 0576 // Use the non-template types below: 0577 // - G4AutoLock with G4Mutex 0578 // - G4RecursiveAutoLock with G4RecursiveMutex 0579 // 0580 // -------------------------------------------------------------------------- // 0581 0582 using G4AutoLock = G4TemplateAutoLock<G4Mutex>; 0583 using G4RecursiveAutoLock = G4TemplateAutoLock<G4RecursiveMutex>; 0584 0585 // provide abbriviated type if another mutex type is desired to be used 0586 // aside from above 0587 template <typename _Tp> 0588 using G4TAutoLock = G4TemplateAutoLock<_Tp>; 0589 0590 #endif // G4AUTOLOCK_HH
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |