Geant4 Cross Reference |
1 // 1 2 // ******************************************* 3 // * License and Disclaimer 4 // * 5 // * The Geant4 software is copyright of th 6 // * the Geant4 Collaboration. It is provided 7 // * conditions of the Geant4 Software License 8 // * LICENSE and available at http://cern.ch/ 9 // * include a list of copyright holders. 10 // * 11 // * Neither the authors of this software syst 12 // * institutes,nor the agencies providing fin 13 // * work make any representation or warran 14 // * regarding this software system or assum 15 // * use. Please see the license in the file 16 // * for the full disclaimer and the limitatio 17 // * 18 // * This code implementation is the result 19 // * technical work of the GEANT4 collaboratio 20 // * By using, copying, modifying or distri 21 // * any work based on the software) you ag 22 // * use in resulting scientific publicati 23 // * acceptance of all terms of the Geant4 Sof 24 // ******************************************* 25 // 26 // G4Autolock 27 // 28 // Class Description: 29 // 30 // This class provides a mechanism to create a 31 // Can be used by applications to implement in 32 // Usage Example: 33 // 34 // #include "G4Threading.hh" 35 // #include "G4AutoLock.hh" 36 // 37 // // defined somewhere -- static so all 38 // static G4Mutex aMutex; 39 // 40 // // somewhere else: 41 // // The G4AutoLock instance will automa 42 // // goes out of scope. One typically de 43 // // there is thread-safe code following 44 // 45 // { 46 // G4AutoLock l(&aMutex); 47 // ProtectedCode(); 48 // } 49 // 50 // UnprotectedCode(); 51 // 52 // // When ProtectedCode() is calling a f 53 // // a normal G4AutoLock + G4Mutex will 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 84 // ------------------------------------------- 85 // Author: Andrea Dotti (15 Feb 2013): First I 86 // 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 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 260 #define G4AUTOLOCK_HH 261 262 #include "G4Threading.hh" 263 264 #include <chrono> 265 #include <iostream> 266 #include <mutex> 267 #include <system_error> 268 269 // Note: Note that G4TemplateAutoLock by itsel 270 // cannot be shared among threads due to 271 // 272 template <typename _Mutex_t> 273 class G4TemplateAutoLock : public std::unique_ 274 { 275 public: 276 //------------------------------------------ 277 // Some useful typedefs 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 { 502 PrintLockErrorMessage(e); 503 } 504 #endif 505 } 506 507 //========================================== 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 { 522 PrintLockErrorMessage(e); 523 } 524 #else 525 suppress_unused_variable(_timeout_duration 526 #endif 527 } 528 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 } 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 574 // ------------------------------------------- 575 // 576 // Use the non-template types below: 577 // - G4AutoLock with G4Mutex 578 // - G4RecursiveAutoLock with G4Recur 579 // 580 // ------------------------------------------- 581 582 using G4AutoLock = G4TemplateAutoLock 583 using G4RecursiveAutoLock = G4TemplateAutoLock 584 585 // provide abbriviated type if another mutex t 586 // aside from above 587 template <typename _Tp> 588 using G4TAutoLock = G4TemplateAutoLock<_Tp>; 589 590 #endif // G4AUTOLOCK_HH 591