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 11.1.3)


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