Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:56

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