Geant4 Cross Reference |
1 // 1 // 2 // ******************************************* 2 // ******************************************************************** 3 // * License and Disclaimer 3 // * License and Disclaimer * 4 // * 4 // * * 5 // * The Geant4 software is copyright of th 5 // * The Geant4 software is copyright of the Copyright Holders of * 6 // * the Geant4 Collaboration. It is provided 6 // * the Geant4 Collaboration. It is provided under the terms and * 7 // * conditions of the Geant4 Software License 7 // * conditions of the Geant4 Software License, included in the file * 8 // * LICENSE and available at http://cern.ch/ 8 // * LICENSE and available at http://cern.ch/geant4/license . These * 9 // * include a list of copyright holders. 9 // * include a list of copyright holders. * 10 // * 10 // * * 11 // * Neither the authors of this software syst 11 // * Neither the authors of this software system, nor their employing * 12 // * institutes,nor the agencies providing fin 12 // * institutes,nor the agencies providing financial support for this * 13 // * work make any representation or warran 13 // * work make any representation or warranty, express or implied, * 14 // * regarding this software system or assum 14 // * regarding this software system or assume any liability for its * 15 // * use. Please see the license in the file 15 // * use. Please see the license in the file LICENSE and URL above * 16 // * for the full disclaimer and the limitatio 16 // * for the full disclaimer and the limitation of liability. * 17 // * 17 // * * 18 // * This code implementation is the result 18 // * This code implementation is the result of the scientific and * 19 // * technical work of the GEANT4 collaboratio 19 // * technical work of the GEANT4 collaboration. * 20 // * By using, copying, modifying or distri 20 // * By using, copying, modifying or distributing the software (or * 21 // * any work based on the software) you ag 21 // * any work based on the software) you agree to acknowledge its * 22 // * use in resulting scientific publicati 22 // * use in resulting scientific publications, and indicate your * 23 // * acceptance of all terms of the Geant4 Sof 23 // * acceptance of all terms of the Geant4 Software license. * 24 // ******************************************* 24 // ******************************************************************** 25 // 25 // 26 // G4Autolock 26 // G4Autolock 27 // 27 // 28 // Class Description: 28 // Class Description: 29 // 29 // 30 // This class provides a mechanism to create a 30 // This class provides a mechanism to create a mutex and locks/unlocks it. 31 // Can be used by applications to implement in 31 // Can be used by applications to implement in a portable way a mutexing logic. 32 // Usage Example: 32 // Usage Example: 33 // 33 // 34 // #include "G4Threading.hh" 34 // #include "G4Threading.hh" 35 // #include "G4AutoLock.hh" 35 // #include "G4AutoLock.hh" 36 // 36 // 37 // // defined somewhere -- static so all 37 // // defined somewhere -- static so all threads see the same mutex 38 // static G4Mutex aMutex; 38 // static G4Mutex aMutex; 39 // 39 // 40 // // somewhere else: 40 // // somewhere else: 41 // // The G4AutoLock instance will automa 41 // // The G4AutoLock instance will automatically unlock the mutex when it 42 // // goes out of scope. One typically de 42 // // goes out of scope. One typically defines the scope within { } if 43 // // there is thread-safe code following 43 // // there is thread-safe code following the auto-lock 44 // 44 // 45 // { 45 // { 46 // G4AutoLock l(&aMutex); 46 // G4AutoLock l(&aMutex); 47 // ProtectedCode(); 47 // ProtectedCode(); 48 // } 48 // } 49 // 49 // 50 // UnprotectedCode(); 50 // UnprotectedCode(); 51 // 51 // 52 // // When ProtectedCode() is calling a f 52 // // When ProtectedCode() is calling a function that also tries to lock 53 // // a normal G4AutoLock + G4Mutex will 53 // // a normal G4AutoLock + G4Mutex will "deadlock". In other words, the 54 // // the mutex in the ProtectedCode() fu 54 // // the mutex in the ProtectedCode() function will wait forever to 55 // // acquire the lock that is being held 55 // // acquire the lock that is being held by the function that called 56 // // ProtectedCode(). In this situation, 56 // // ProtectedCode(). In this situation, use a G4RecursiveAutoLock + 57 // // G4RecursiveMutex, e.g. 57 // // G4RecursiveMutex, e.g. 58 // 58 // 59 // // defined somewhere -- static so all 59 // // defined somewhere -- static so all threads see the same mutex 60 // static G4RecursiveMutex aRecursiveMute 60 // static G4RecursiveMutex aRecursiveMutex; 61 // 61 // 62 // // this function is sometimes called d 62 // // this function is sometimes called directly and sometimes called 63 // // from SomeFunction_B(), which also l 63 // // from SomeFunction_B(), which also locks the mutex 64 // void SomeFunction_A() 64 // void SomeFunction_A() 65 // { 65 // { 66 // // when called from SomeFunction_B 66 // // when called from SomeFunction_B(), a G4Mutex + G4AutoLock will 67 // // deadlock 67 // // deadlock 68 // G4RecursiveAutoLock l(&aRecursiveM 68 // G4RecursiveAutoLock l(&aRecursiveMutex); 69 // // do something 69 // // do something 70 // } 70 // } 71 // 71 // 72 // void SomeFunction_B() 72 // void SomeFunction_B() 73 // { 73 // { 74 // 74 // 75 // { 75 // { 76 // G4RecursiveAutoLock l(&aRecurs 76 // G4RecursiveAutoLock l(&aRecursiveMutex); 77 // SomeFunction_A(); 77 // SomeFunction_A(); 78 // } 78 // } 79 // 79 // 80 // UnprotectedCode(); 80 // UnprotectedCode(); 81 // } 81 // } 82 // 82 // 83 83 84 // ------------------------------------------- 84 // -------------------------------------------------------------------- 85 // Author: Andrea Dotti (15 Feb 2013): First I 85 // Author: Andrea Dotti (15 Feb 2013): First Implementation 86 // 86 // 87 // Update: Jonathan Madsen (9 Feb 2018): Repla 87 // Update: Jonathan Madsen (9 Feb 2018): Replaced custom implementation 88 // with inheritance from C++11 unique_loc 88 // with inheritance from C++11 unique_lock, which inherits the 89 // following member functions: 89 // following member functions: 90 // 90 // 91 // - unique_lock(unique_lock&& other) noe 91 // - unique_lock(unique_lock&& other) noexcept; 92 // - explicit unique_lock(mutex_type& m); 92 // - explicit unique_lock(mutex_type& m); 93 // - unique_lock(mutex_type& m, std::defe 93 // - unique_lock(mutex_type& m, std::defer_lock_t t) noexcept; 94 // - unique_lock(mutex_type& m, std::try_ 94 // - unique_lock(mutex_type& m, std::try_to_lock_t t); 95 // - unique_lock(mutex_type& m, std::adop 95 // - unique_lock(mutex_type& m, std::adopt_lock_t t); 96 // 96 // 97 // - template <typename Rep, typename Per 97 // - template <typename Rep, typename Period> 98 // unique_lock(mutex_type& m, 98 // unique_lock(mutex_type& m, 99 // const std::chrono::durati 99 // const std::chrono::duration<Rep,Period>& timeout_duration); 100 // 100 // 101 // - template<typename Clock, typename Du 101 // - template<typename Clock, typename Duration> 102 // unique_lock(mutex_type& m, 102 // unique_lock(mutex_type& m, 103 // const std::chrono::time_point< 103 // const std::chrono::time_point<Clock,Duration>& timeout_time); 104 // 104 // 105 // - void lock(); 105 // - void lock(); 106 // - void unlock(); 106 // - void unlock(); 107 // - bool try_lock(); 107 // - bool try_lock(); 108 // 108 // 109 // - template <typename Rep, typename Per 109 // - template <typename Rep, typename Period> 110 // bool try_lock_for(const std::chrono: 110 // bool try_lock_for(const std::chrono::duration<Rep,Period>&); 111 // 111 // 112 // - template <typename Rep, typename Per 112 // - template <typename Rep, typename Period> 113 // bool try_lock_until(const std::chron 113 // bool try_lock_until(const std::chrono::time_point<Clock,Duration>&); 114 // 114 // 115 // - void swap(unique_lock& other) noexce 115 // - void swap(unique_lock& other) noexcept; 116 // - mutex_type* release() noexcept; 116 // - mutex_type* release() noexcept; 117 // - mutex_type* mutex() const noexcept; 117 // - mutex_type* mutex() const noexcept; 118 // - bool owns_lock() const noexcept; 118 // - bool owns_lock() const noexcept; 119 // - explicit operator bool() const noexc 119 // - explicit operator bool() const noexcept; 120 // - unique_lock& operator=(unique_lock&& 120 // - unique_lock& operator=(unique_lock&& other); 121 // 121 // 122 // ------------------------------------------- 122 // -------------------------------------------------------------------- 123 // 123 // 124 // Note that G4AutoLock is defined also for a 124 // Note that G4AutoLock is defined also for a sequential Geant4 build but below 125 // regarding implementation (also found in G4T 125 // regarding implementation (also found in G4Threading.hh) 126 // 126 // 127 // 127 // 128 // NOTE ON GEANT4 SERIAL BUILDS AND M 128 // NOTE ON GEANT4 SERIAL BUILDS AND MUTEX/UNIQUE_LOCK 129 // ================================== 129 // ================================================== 130 // 130 // 131 // G4Mutex and G4RecursiveMutex are always C++ 131 // G4Mutex and G4RecursiveMutex are always C++11 std::mutex types 132 // however, in serial mode, using G4MUTEXLOCK 132 // however, in serial mode, using G4MUTEXLOCK and G4MUTEXUNLOCK on these 133 // types has no effect -- i.e. the mutexes are 133 // types has no effect -- i.e. the mutexes are not actually locked or unlocked 134 // 134 // 135 // Additionally, when a G4Mutex or G4Recursive 135 // Additionally, when a G4Mutex or G4RecursiveMutex is used with G4AutoLock 136 // and G4RecursiveAutoLock, respectively, thes 136 // and G4RecursiveAutoLock, respectively, these classes also suppressing 137 // the locking and unlocking of the mutex. Reg 137 // the locking and unlocking of the mutex. Regardless of the build type, 138 // G4AutoLock and G4RecursiveAutoLock inherit 138 // G4AutoLock and G4RecursiveAutoLock inherit from std::unique_lock<std::mutex> 139 // and std::unique_lock<std::recursive_mutex>, 139 // and std::unique_lock<std::recursive_mutex>, respectively. This means 140 // that in situations (such as is needed by th 140 // that in situations (such as is needed by the analysis category), the 141 // G4AutoLock and G4RecursiveAutoLock can be p 141 // G4AutoLock and G4RecursiveAutoLock can be passed to functions requesting 142 // a std::unique_lock. Within these functions, 142 // a std::unique_lock. Within these functions, since std::unique_lock 143 // member functions are not virtual, they will 143 // member functions are not virtual, they will not retain the dummy locking 144 // and unlocking behavior 144 // and unlocking behavior 145 // --> An example of this behavior can be foun 145 // --> An example of this behavior can be found below 146 // 146 // 147 // Jonathan R. Madsen (February 21, 2018) 147 // Jonathan R. Madsen (February 21, 2018) 148 // 148 // 149 /** 149 /** 150 150 151 //============================================ 151 //============================================================================// 152 152 153 void print_threading() 153 void print_threading() 154 { 154 { 155 #ifdef G4MULTITHREADED 155 #ifdef G4MULTITHREADED 156 std::cout << "\nUsing G4MULTITHREADED vers 156 std::cout << "\nUsing G4MULTITHREADED version..." << std::endl; 157 #else 157 #else 158 std::cout << "\nUsing G4SERIAL version..." 158 std::cout << "\nUsing G4SERIAL version..." << std::endl; 159 #endif 159 #endif 160 } 160 } 161 161 162 //============================================ 162 //============================================================================// 163 163 164 typedef std::unique_lock<std::mutex> unique_lo 164 typedef std::unique_lock<std::mutex> unique_lock_t; 165 // functions for casting G4AutoLock to std::un 165 // functions for casting G4AutoLock to std::unique_lock to demonstrate 166 // that G4AutoLock is NOT polymorphic 166 // that G4AutoLock is NOT polymorphic 167 void as_unique_lock(unique_lock_t* lock) { loc 167 void as_unique_lock(unique_lock_t* lock) { lock->lock(); } 168 void as_unique_unlock(unique_lock_t* lock) { l 168 void as_unique_unlock(unique_lock_t* lock) { lock->unlock(); } 169 169 170 //============================================ 170 //============================================================================// 171 171 172 void run(const uint64_t& n) 172 void run(const uint64_t& n) 173 { 173 { 174 // sync the threads a bit 174 // sync the threads a bit 175 std::this_thread::sleep_for(std::chrono::m 175 std::this_thread::sleep_for(std::chrono::milliseconds(10)); 176 176 177 // get two mutexes to avoid deadlock when 177 // get two mutexes to avoid deadlock when l32 actually locks 178 G4AutoLock l32(G4TypeMutex<int32_t>(), std 178 G4AutoLock l32(G4TypeMutex<int32_t>(), std::defer_lock); 179 G4AutoLock l64(G4TypeMutex<int64_t>(), std 179 G4AutoLock l64(G4TypeMutex<int64_t>(), std::defer_lock); 180 180 181 // when serial: will not execute std::uniq 181 // when serial: will not execute std::unique_lock::lock() because 182 // it overrides the member function 182 // it overrides the member function 183 l32.lock(); 183 l32.lock(); 184 // regardless of serial or MT: will execut 184 // regardless of serial or MT: will execute std::unique_lock::lock() 185 // because std::unique_lock::lock() is not 185 // because std::unique_lock::lock() is not virtual 186 as_unique_lock(&l64); 186 as_unique_lock(&l64); 187 187 188 std::cout << "Running iteration " << n << 188 std::cout << "Running iteration " << n << "..." << std::endl; 189 } 189 } 190 190 191 //============================================ 191 //============================================================================// 192 // execute some work 192 // execute some work 193 template <typename thread_type = std::thread> 193 template <typename thread_type = std::thread> 194 void exec(uint64_t n) 194 void exec(uint64_t n) 195 { 195 { 196 // get two mutexes to avoid deadlock when 196 // get two mutexes to avoid deadlock when l32 actually locks 197 G4AutoLock l32(G4TypeMutex<int32_t>(), std 197 G4AutoLock l32(G4TypeMutex<int32_t>(), std::defer_lock); 198 G4AutoLock l64(G4TypeMutex<int64_t>(), std 198 G4AutoLock l64(G4TypeMutex<int64_t>(), std::defer_lock); 199 199 200 std::vector<thread_type*> threads(n, nullp 200 std::vector<thread_type*> threads(n, nullptr); 201 for(uint64_t i = 0; i < n; ++i) 201 for(uint64_t i = 0; i < n; ++i) 202 { 202 { 203 threads[i] = new thread_type(); 203 threads[i] = new thread_type(); 204 *(threads[i]) = std::move(thread_type( 204 *(threads[i]) = std::move(thread_type(run, i)); 205 } 205 } 206 206 207 // when serial: will not execute std::uniq 207 // when serial: will not execute std::unique_lock::lock() because 208 // it overrides the member function 208 // it overrides the member function 209 l32.lock(); 209 l32.lock(); 210 // regardless of serial or MT: will execut 210 // regardless of serial or MT: will execute std::unique_lock::lock() 211 // because std::unique_lock::lock() is not 211 // because std::unique_lock::lock() is not virtual 212 as_unique_lock(&l64); 212 as_unique_lock(&l64); 213 213 214 std::cout << "Joining..." << std::endl; 214 std::cout << "Joining..." << std::endl; 215 215 216 // when serial: will not execute std::uniq 216 // when serial: will not execute std::unique_lock::unlock() because 217 // it overrides the member function 217 // it overrides the member function 218 l32.unlock(); 218 l32.unlock(); 219 // regardless of serial or MT: will execut 219 // regardless of serial or MT: will execute std::unique_lock::unlock() 220 // because std::unique_lock::unlock() is n 220 // because std::unique_lock::unlock() is not virtual 221 as_unique_unlock(&l64); 221 as_unique_unlock(&l64); 222 222 223 // NOTE ABOUT UNLOCKS: 223 // NOTE ABOUT UNLOCKS: 224 // in MT, commenting out either 224 // in MT, commenting out either 225 // l32.unlock(); 225 // l32.unlock(); 226 // or 226 // or 227 // as_unique_unlock(&l64); 227 // as_unique_unlock(&l64); 228 // creates a deadlock; in serial, commenti 228 // creates a deadlock; in serial, commenting out 229 // as_unique_unlock(&l64); 229 // as_unique_unlock(&l64); 230 // creates a deadlock but commenting out 230 // creates a deadlock but commenting out 231 // l32.unlock(); 231 // l32.unlock(); 232 // does not 232 // does not 233 233 234 // clean up and join 234 // clean up and join 235 for(uint64_t i = 0; i < n; ++i) 235 for(uint64_t i = 0; i < n; ++i) 236 { 236 { 237 threads[i]->join(); 237 threads[i]->join(); 238 delete threads[i]; 238 delete threads[i]; 239 } 239 } 240 threads.clear(); 240 threads.clear(); 241 } 241 } 242 242 243 //============================================ 243 //============================================================================// 244 244 245 int main() 245 int main() 246 { 246 { 247 print_threading(); 247 print_threading(); 248 248 249 uint64_t n = 30; 249 uint64_t n = 30; 250 std::cout << "\nRunning with real threads. 250 std::cout << "\nRunning with real threads...\n" << std::endl; 251 exec<std::thread>(n); 251 exec<std::thread>(n); 252 std::cout << "\nRunning with fake threads. 252 std::cout << "\nRunning with fake threads...\n" << std::endl; 253 exec<G4DummyThread>(n); 253 exec<G4DummyThread>(n); 254 254 255 } 255 } 256 256 257 **/ 257 **/ 258 // ------------------------------------------- 258 // -------------------------------------------------------------------- 259 #ifndef G4AUTOLOCK_HH 259 #ifndef G4AUTOLOCK_HH 260 #define G4AUTOLOCK_HH 260 #define G4AUTOLOCK_HH 261 261 262 #include "G4Threading.hh" 262 #include "G4Threading.hh" 263 263 264 #include <chrono> 264 #include <chrono> 265 #include <iostream> 265 #include <iostream> 266 #include <mutex> 266 #include <mutex> 267 #include <system_error> 267 #include <system_error> 268 268 269 // Note: Note that G4TemplateAutoLock by itsel 269 // Note: Note that G4TemplateAutoLock by itself is not thread-safe and 270 // cannot be shared among threads due to 270 // cannot be shared among threads due to the locked switch 271 // 271 // 272 template <typename _Mutex_t> 272 template <typename _Mutex_t> 273 class G4TemplateAutoLock : public std::unique_ 273 class G4TemplateAutoLock : public std::unique_lock<_Mutex_t> 274 { 274 { 275 public: 275 public: 276 //------------------------------------------ 276 //------------------------------------------------------------------------// 277 // Some useful typedefs 277 // Some useful typedefs 278 //------------------------------------------ 278 //------------------------------------------------------------------------// 279 using unique_lock_t = std::unique_lock<_Mute << 279 typedef std::unique_lock<_Mutex_t> unique_lock_t; 280 using this_type = G4TemplateAutoLock<_Mutex_ << 280 typedef G4TemplateAutoLock<_Mutex_t> this_type; 281 using mutex_type = typename unique_lock_t::m << 281 typedef typename unique_lock_t::mutex_type mutex_type; 282 282 283 public: 283 public: 284 //------------------------------------------ 284 //------------------------------------------------------------------------// 285 // STL-consistent reference form constructor 285 // STL-consistent reference form constructors 286 //------------------------------------------ 286 //------------------------------------------------------------------------// 287 287 288 // reference form is consistent with STL loc 288 // reference form is consistent with STL lock_guard types 289 // Locks the associated mutex by calling m.l 289 // Locks the associated mutex by calling m.lock(). The behavior is 290 // undefined if the current thread already o 290 // undefined if the current thread already owns the mutex except when 291 // the mutex is recursive 291 // the mutex is recursive 292 G4TemplateAutoLock(mutex_type& _mutex) 292 G4TemplateAutoLock(mutex_type& _mutex) 293 : unique_lock_t(_mutex, std::defer_lock) 293 : unique_lock_t(_mutex, std::defer_lock) 294 { 294 { 295 // call termination-safe locking. if seria 295 // call termination-safe locking. if serial, this call has no effect 296 _lock_deferred(); 296 _lock_deferred(); 297 } 297 } 298 298 299 // Tries to lock the associated mutex by cal 299 // Tries to lock the associated mutex by calling 300 // m.try_lock_for(_timeout_duration). Blocks 300 // m.try_lock_for(_timeout_duration). Blocks until specified 301 // _timeout_duration has elapsed or the lock 301 // _timeout_duration has elapsed or the lock is acquired, whichever comes 302 // first. May block for longer than _timeout 302 // first. May block for longer than _timeout_duration. 303 template <typename Rep, typename Period> 303 template <typename Rep, typename Period> 304 G4TemplateAutoLock( 304 G4TemplateAutoLock( 305 mutex_type& _mutex, 305 mutex_type& _mutex, 306 const std::chrono::duration<Rep, Period>& 306 const std::chrono::duration<Rep, Period>& _timeout_duration) 307 : unique_lock_t(_mutex, std::defer_lock) 307 : unique_lock_t(_mutex, std::defer_lock) 308 { 308 { 309 // call termination-safe locking. if seria 309 // call termination-safe locking. if serial, this call has no effect 310 _lock_deferred(_timeout_duration); 310 _lock_deferred(_timeout_duration); 311 } 311 } 312 312 313 // Tries to lock the associated mutex by cal 313 // Tries to lock the associated mutex by calling 314 // m.try_lock_until(_timeout_time). Blocks u 314 // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has 315 // been reached or the lock is acquired, whi 315 // been reached or the lock is acquired, whichever comes first. May block 316 // for longer than until _timeout_time has b 316 // for longer than until _timeout_time has been reached. 317 template <typename Clock, typename Duration> 317 template <typename Clock, typename Duration> 318 G4TemplateAutoLock( 318 G4TemplateAutoLock( 319 mutex_type& _mutex, 319 mutex_type& _mutex, 320 const std::chrono::time_point<Clock, Durat 320 const std::chrono::time_point<Clock, Duration>& _timeout_time) 321 : unique_lock_t(_mutex, std::defer_lock) 321 : unique_lock_t(_mutex, std::defer_lock) 322 { 322 { 323 // call termination-safe locking. if seria 323 // call termination-safe locking. if serial, this call has no effect 324 _lock_deferred(_timeout_time); 324 _lock_deferred(_timeout_time); 325 } 325 } 326 326 327 // Does not lock the associated mutex. 327 // Does not lock the associated mutex. 328 G4TemplateAutoLock(mutex_type& _mutex, std:: 328 G4TemplateAutoLock(mutex_type& _mutex, std::defer_lock_t _lock) noexcept 329 : unique_lock_t(_mutex, _lock) 329 : unique_lock_t(_mutex, _lock) 330 {} 330 {} 331 331 332 #ifdef G4MULTITHREADED 332 #ifdef G4MULTITHREADED 333 333 334 // Tries to lock the associated mutex withou 334 // Tries to lock the associated mutex without blocking by calling 335 // m.try_lock(). The behavior is undefined i 335 // m.try_lock(). The behavior is undefined if the current thread already 336 // owns the mutex except when the mutex is r 336 // owns the mutex except when the mutex is recursive. 337 G4TemplateAutoLock(mutex_type& _mutex, std:: 337 G4TemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t _lock) 338 : unique_lock_t(_mutex, _lock) 338 : unique_lock_t(_mutex, _lock) 339 {} 339 {} 340 340 341 // Assumes the calling thread already owns m 341 // Assumes the calling thread already owns m 342 G4TemplateAutoLock(mutex_type& _mutex, std:: 342 G4TemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t _lock) 343 : unique_lock_t(_mutex, _lock) 343 : unique_lock_t(_mutex, _lock) 344 {} 344 {} 345 345 346 #else 346 #else 347 347 348 // serial dummy version (initializes unique_ 348 // serial dummy version (initializes unique_lock but does not lock) 349 G4TemplateAutoLock(mutex_type& _mutex, std:: 349 G4TemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t) 350 : unique_lock_t(_mutex, std::defer_lock) 350 : unique_lock_t(_mutex, std::defer_lock) 351 {} 351 {} 352 352 353 // serial dummy version (initializes unique_ 353 // serial dummy version (initializes unique_lock but does not lock) 354 G4TemplateAutoLock(mutex_type& _mutex, std:: 354 G4TemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t) 355 : unique_lock_t(_mutex, std::defer_lock) 355 : unique_lock_t(_mutex, std::defer_lock) 356 {} 356 {} 357 357 358 #endif // defined(G4MULTITHREADED) 358 #endif // defined(G4MULTITHREADED) 359 359 360 public: 360 public: 361 //------------------------------------------ 361 //------------------------------------------------------------------------// 362 // Backwards compatibility versions (constru 362 // Backwards compatibility versions (constructor with pointer to mutex) 363 //------------------------------------------ 363 //------------------------------------------------------------------------// 364 G4TemplateAutoLock(mutex_type* _mutex) 364 G4TemplateAutoLock(mutex_type* _mutex) 365 : unique_lock_t(*_mutex, std::defer_lock) 365 : unique_lock_t(*_mutex, std::defer_lock) 366 { 366 { 367 // call termination-safe locking. if seria 367 // call termination-safe locking. if serial, this call has no effect 368 _lock_deferred(); 368 _lock_deferred(); 369 } 369 } 370 370 371 G4TemplateAutoLock(mutex_type* _mutex, std:: 371 G4TemplateAutoLock(mutex_type* _mutex, std::defer_lock_t _lock) noexcept 372 : unique_lock_t(*_mutex, _lock) 372 : unique_lock_t(*_mutex, _lock) 373 {} 373 {} 374 374 375 #if defined(G4MULTITHREADED) 375 #if defined(G4MULTITHREADED) 376 376 377 G4TemplateAutoLock(mutex_type* _mutex, std:: 377 G4TemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t _lock) 378 : unique_lock_t(*_mutex, _lock) 378 : unique_lock_t(*_mutex, _lock) 379 {} 379 {} 380 380 381 G4TemplateAutoLock(mutex_type* _mutex, std:: 381 G4TemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t _lock) 382 : unique_lock_t(*_mutex, _lock) 382 : unique_lock_t(*_mutex, _lock) 383 {} 383 {} 384 384 385 #else // NOT defined(G4MULTITHREADED) -- i.e. 385 #else // NOT defined(G4MULTITHREADED) -- i.e. serial 386 386 387 G4TemplateAutoLock(mutex_type* _mutex, std:: 387 G4TemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t) 388 : unique_lock_t(*_mutex, std::defer_lock) 388 : unique_lock_t(*_mutex, std::defer_lock) 389 {} 389 {} 390 390 391 G4TemplateAutoLock(mutex_type* _mutex, std:: 391 G4TemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t) 392 : unique_lock_t(*_mutex, std::defer_lock) 392 : unique_lock_t(*_mutex, std::defer_lock) 393 {} 393 {} 394 394 395 #endif // defined(G4MULTITHREADED) 395 #endif // defined(G4MULTITHREADED) 396 396 397 public: 397 public: 398 //------------------------------------------ 398 //------------------------------------------------------------------------// 399 // Non-constructor overloads 399 // Non-constructor overloads 400 //------------------------------------------ 400 //------------------------------------------------------------------------// 401 401 402 #if defined(G4MULTITHREADED) 402 #if defined(G4MULTITHREADED) 403 403 404 // overload nothing 404 // overload nothing 405 405 406 #else // NOT defined(G4MULTITHREADED) -- i.e. 406 #else // NOT defined(G4MULTITHREADED) -- i.e. serial 407 407 408 // override unique lock member functions to 408 // override unique lock member functions to keep from locking/unlocking 409 // but does not override in polymorphic usag 409 // but does not override in polymorphic usage 410 void lock() {} 410 void lock() {} 411 void unlock() {} 411 void unlock() {} 412 bool try_lock() { return true; } 412 bool try_lock() { return true; } 413 413 414 template <typename Rep, typename Period> 414 template <typename Rep, typename Period> 415 bool try_lock_for(const std::chrono::duratio 415 bool try_lock_for(const std::chrono::duration<Rep, Period>&) 416 { 416 { 417 return true; 417 return true; 418 } 418 } 419 419 420 template <typename Clock, typename Duration> 420 template <typename Clock, typename Duration> 421 bool try_lock_until(const std::chrono::time_ 421 bool try_lock_until(const std::chrono::time_point<Clock, Duration>&) 422 { 422 { 423 return true; 423 return true; 424 } 424 } 425 425 426 void swap(this_type& other) noexcept { std:: 426 void swap(this_type& other) noexcept { std::swap(*this, other); } 427 bool owns_lock() const noexcept { return fal 427 bool owns_lock() const noexcept { return false; } 428 428 429 // no need to overload 429 // no need to overload 430 // explicit operator bool() const noexcept; 430 // explicit operator bool() const noexcept; 431 // this_type& operator=(this_type&& other); 431 // this_type& operator=(this_type&& other); 432 // mutex_type* release() noexcept; 432 // mutex_type* release() noexcept; 433 // mutex_type* mutex() const noexcept; 433 // mutex_type* mutex() const noexcept; 434 434 435 #endif // defined(G4MULTITHREADED) 435 #endif // defined(G4MULTITHREADED) 436 436 437 private: 437 private: 438 // helpful macros 438 // helpful macros 439 #define _is_stand_mutex(_Tp) (std::is_same<_Tp 439 #define _is_stand_mutex(_Tp) (std::is_same<_Tp, G4Mutex>::value) 440 #define _is_recur_mutex(_Tp) (std::is_same<_Tp 440 #define _is_recur_mutex(_Tp) (std::is_same<_Tp, G4RecursiveMutex>::value) 441 #define _is_other_mutex(_Tp) (!_is_stand_mutex 441 #define _is_other_mutex(_Tp) (!_is_stand_mutex(_Tp) && !_is_recur_mutex(_Tp)) 442 442 443 template <typename _Tp 443 template <typename _Tp = _Mutex_t, 444 typename std::enable_if<_is_stand_ 444 typename std::enable_if<_is_stand_mutex(_Tp), int>::type = 0> 445 std::string GetTypeString() 445 std::string GetTypeString() 446 { 446 { 447 return "G4AutoLock<G4Mutex>"; 447 return "G4AutoLock<G4Mutex>"; 448 } 448 } 449 449 450 template <typename _Tp 450 template <typename _Tp = _Mutex_t, 451 typename std::enable_if<_is_recur_ 451 typename std::enable_if<_is_recur_mutex(_Tp), int>::type = 0> 452 std::string GetTypeString() 452 std::string GetTypeString() 453 { 453 { 454 return "G4AutoLock<G4RecursiveMutex>"; 454 return "G4AutoLock<G4RecursiveMutex>"; 455 } 455 } 456 456 457 template <typename _Tp 457 template <typename _Tp = _Mutex_t, 458 typename std::enable_if<_is_other_ 458 typename std::enable_if<_is_other_mutex(_Tp), int>::type = 0> 459 std::string GetTypeString() 459 std::string GetTypeString() 460 { 460 { 461 return "G4AutoLock<UNKNOWN_MUTEX>"; 461 return "G4AutoLock<UNKNOWN_MUTEX>"; 462 } 462 } 463 463 464 // pollution is bad 464 // pollution is bad 465 #undef _is_stand_mutex 465 #undef _is_stand_mutex 466 #undef _is_recur_mutex 466 #undef _is_recur_mutex 467 #undef _is_other_mutex 467 #undef _is_other_mutex 468 468 469 // used in _lock_deferred chrono variants to 469 // used in _lock_deferred chrono variants to avoid ununsed-variable warning 470 template <typename _Tp> 470 template <typename _Tp> 471 void suppress_unused_variable(const _Tp&) 471 void suppress_unused_variable(const _Tp&) 472 {} 472 {} 473 473 474 //========================================== 474 //========================================================================// 475 // NOTE on _lock_deferred(...) variants: 475 // NOTE on _lock_deferred(...) variants: 476 // a system_error in lock means that th 476 // a system_error in lock means that the mutex is unavailable 477 // we want to throw the error that come 477 // we want to throw the error that comes from locking an unavailable 478 // mutex so that we know there is a mem 478 // mutex so that we know there is a memory leak 479 // if the mutex is valid, this will hol 479 // if the mutex is valid, this will hold until the other thread 480 // finishes 480 // finishes 481 481 482 // sometimes certain destructors use locks, 482 // sometimes certain destructors use locks, this isn't an issue unless 483 // the object is leaked. When this occurs, t 483 // the object is leaked. When this occurs, the application finalization 484 // (i.e. the real or implied "return 0" part 484 // (i.e. the real or implied "return 0" part of main) will call destructors 485 // on Geant4 object after some static mutex 485 // on Geant4 object after some static mutex variables are deleted, leading 486 // to the error code (typically on Clang com 486 // to the error code (typically on Clang compilers): 487 // libc++abi.dylib: terminating with un 487 // libc++abi.dylib: terminating with uncaught exception of type 488 // std::__1::system_error: mutex lock f 488 // std::__1::system_error: mutex lock failed: Invalid argument 489 // this function protects against this failu 489 // this function protects against this failure until such a time that 490 // these issues have been resolved 490 // these issues have been resolved 491 491 492 //========================================== 492 //========================================================================// 493 // standard locking 493 // standard locking 494 inline void _lock_deferred() 494 inline void _lock_deferred() 495 { 495 { 496 #if defined(G4MULTITHREADED) 496 #if defined(G4MULTITHREADED) 497 try 497 try 498 { 498 { 499 this->unique_lock_t::lock(); 499 this->unique_lock_t::lock(); 500 } catch(std::system_error& e) 500 } catch(std::system_error& e) 501 { 501 { 502 PrintLockErrorMessage(e); 502 PrintLockErrorMessage(e); 503 } 503 } 504 #endif 504 #endif 505 } 505 } 506 506 507 //========================================== 507 //========================================================================// 508 // Tries to lock the associated mutex by cal 508 // Tries to lock the associated mutex by calling 509 // m.try_lock_for(_timeout_duration). Blocks 509 // m.try_lock_for(_timeout_duration). Blocks until specified 510 // _timeout_duration has elapsed or the lock 510 // _timeout_duration has elapsed or the lock is acquired, whichever comes 511 // first. May block for longer than _timeout 511 // first. May block for longer than _timeout_duration. 512 template <typename Rep, typename Period> 512 template <typename Rep, typename Period> 513 void _lock_deferred( 513 void _lock_deferred( 514 const std::chrono::duration<Rep, Period>& 514 const std::chrono::duration<Rep, Period>& _timeout_duration) 515 { 515 { 516 #if defined(G4MULTITHREADED) 516 #if defined(G4MULTITHREADED) 517 try 517 try 518 { 518 { 519 this->unique_lock_t::try_lock_for(_timeo 519 this->unique_lock_t::try_lock_for(_timeout_duration); 520 } catch(std::system_error& e) 520 } catch(std::system_error& e) 521 { 521 { 522 PrintLockErrorMessage(e); 522 PrintLockErrorMessage(e); 523 } 523 } 524 #else 524 #else 525 suppress_unused_variable(_timeout_duration 525 suppress_unused_variable(_timeout_duration); 526 #endif 526 #endif 527 } 527 } 528 528 529 //========================================== 529 //========================================================================// 530 // Tries to lock the associated mutex by cal 530 // Tries to lock the associated mutex by calling 531 // m.try_lock_until(_timeout_time). Blocks u 531 // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has 532 // been reached or the lock is acquired, whi 532 // been reached or the lock is acquired, whichever comes first. May block 533 // for longer than until _timeout_time has b 533 // for longer than until _timeout_time has been reached. 534 template <typename Clock, typename Duration> 534 template <typename Clock, typename Duration> 535 void _lock_deferred( 535 void _lock_deferred( 536 const std::chrono::time_point<Clock, Durat 536 const std::chrono::time_point<Clock, Duration>& _timeout_time) 537 { 537 { 538 #if defined(G4MULTITHREADED) 538 #if defined(G4MULTITHREADED) 539 try 539 try 540 { 540 { 541 this->unique_lock_t::try_lock_until(_tim 541 this->unique_lock_t::try_lock_until(_timeout_time); 542 } catch(std::system_error& e) 542 } catch(std::system_error& e) 543 { 543 { 544 PrintLockErrorMessage(e); 544 PrintLockErrorMessage(e); 545 } 545 } 546 #else 546 #else 547 suppress_unused_variable(_timeout_time); 547 suppress_unused_variable(_timeout_time); 548 #endif 548 #endif 549 } 549 } 550 550 551 //========================================== 551 //========================================================================// 552 // the message for what mutex lock fails due 552 // the message for what mutex lock fails due to deleted static mutex 553 // at termination 553 // at termination 554 void PrintLockErrorMessage(std::system_error 554 void PrintLockErrorMessage(std::system_error& e) 555 { 555 { 556 // use std::cout/std::endl to avoid includ 556 // use std::cout/std::endl to avoid include dependencies 557 using std::cout; 557 using std::cout; 558 using std::endl; 558 using std::endl; 559 // the error that comes from locking an un 559 // the error that comes from locking an unavailable mutex 560 #if defined(G4VERBOSE) 560 #if defined(G4VERBOSE) 561 cout << "Non-critical error: mutex lock fa 561 cout << "Non-critical error: mutex lock failure in " 562 << GetTypeString<mutex_type>() << ". 562 << GetTypeString<mutex_type>() << ". " 563 << "If the app is terminating, Geant4 563 << "If the app is terminating, Geant4 failed to " 564 << "delete an allocated resource and 564 << "delete an allocated resource and a Geant4 destructor is " 565 << "being called after the statics we 565 << "being called after the statics were destroyed. \n\t--> " 566 << "Exception: [code: " << e.code() < 566 << "Exception: [code: " << e.code() << "] caught: " << e.what() 567 << endl; 567 << endl; 568 #else 568 #else 569 suppress_unused_variable(e); 569 suppress_unused_variable(e); 570 #endif 570 #endif 571 } 571 } 572 }; 572 }; 573 573 574 // ------------------------------------------- 574 // -------------------------------------------------------------------------- // 575 // 575 // 576 // Use the non-template types below: 576 // Use the non-template types below: 577 // - G4AutoLock with G4Mutex 577 // - G4AutoLock with G4Mutex 578 // - G4RecursiveAutoLock with G4Recur 578 // - G4RecursiveAutoLock with G4RecursiveMutex 579 // 579 // 580 // ------------------------------------------- 580 // -------------------------------------------------------------------------- // 581 581 582 using G4AutoLock = G4TemplateAutoLock 582 using G4AutoLock = G4TemplateAutoLock<G4Mutex>; 583 using G4RecursiveAutoLock = G4TemplateAutoLock 583 using G4RecursiveAutoLock = G4TemplateAutoLock<G4RecursiveMutex>; 584 584 585 // provide abbriviated type if another mutex t 585 // provide abbriviated type if another mutex type is desired to be used 586 // aside from above 586 // aside from above 587 template <typename _Tp> 587 template <typename _Tp> 588 using G4TAutoLock = G4TemplateAutoLock<_Tp>; 588 using G4TAutoLock = G4TemplateAutoLock<_Tp>; 589 589 590 #endif // G4AUTOLOCK_HH 590 #endif // G4AUTOLOCK_HH 591 591