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