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 ]

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