Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/global/management/include/G4AutoLock.hh

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

Diff markup

Differences between /global/management/include/G4AutoLock.hh (Version 11.3.0) and /global/management/include/G4AutoLock.hh (Version 10.6)


  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