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