Geant4  v4-10.4-release
 모두 클래스 네임스페이스들 파일들 함수 변수 타입정의 열거형 타입 열거형 멤버 Friends 매크로 그룹들 페이지들
G4AutoLock.hh
이 파일의 문서화 페이지로 가기
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 // $Id$
27 //
28 // ---------------------------------------------------------------
29 // GEANT 4 class header file
30 //
31 // Class Description:
32 //
33 // This class provides a mechanism to create a mutex and locks/unlocks it.
34 // Can be used by applications to implement in a portable way a mutexing logic.
35 // Usage Example:
36 //
37 // #include "G4Threading.hh"
38 // #include "G4AutoLock.hh"
39 //
40 // // defined somewhere -- static so all threads see the same mutex
41 // static G4Mutex aMutex;
42 //
43 // // somewhere else:
44 // // The G4AutoLock instance will automatically unlock the mutex when it
45 // // goes out of scope. One typically defines the scope within { } if
46 // // there is thread-safe code following the auto-lock
47 //
48 // {
49 // G4AutoLock l(&aMutex);
50 // ProtectedCode();
51 // }
52 //
53 // UnprotectedCode();
54 //
55 // // When ProtectedCode() is calling a function that also tries to lock
56 // // a normal G4AutoLock + G4Mutex will "deadlock". In other words, the
57 // // the mutex in the ProtectedCode() function will wait forever to
58 // // acquire the lock that is being held by the function that called
59 // // ProtectedCode(). In this situation, use a G4RecursiveAutoLock +
60 // // G4RecursiveMutex, e.g.
61 //
62 // // defined somewhere -- static so all threads see the same mutex
63 // static G4RecursiveMutex aRecursiveMutex;
64 //
65 // // this function is sometimes called directly and sometimes called
66 // // from SomeFunction_B(), which also locks the mutex
67 // void SomeFunction_A()
68 // {
69 // // when called from SomeFunction_B(), a G4Mutex + G4AutoLock will
70 // // deadlock
71 // G4RecursiveAutoLock l(&aRecursiveMutex);
72 // // do something
73 // }
74 //
75 // void SomeFunction_B()
76 // {
77 //
78 // {
79 // G4RecursiveAutoLock l(&aRecursiveMutex);
80 // SomeFunction_A();
81 // }
82 //
83 // UnprotectedCode();
84 // }
85 //
86 //
87 // ---------------------------------------------------------------
88 // Author: Andrea Dotti (15 Feb 2013): First Implementation
89 //
90 // Update: Jonathan Madsen (9 Feb 2018): Replaced custom implementation
91 // with inheritance from C++11 unique_lock, which inherits the
92 // following member functions:
93 //
94 // - unique_lock(unique_lock&& other) noexcept;
95 // - explicit unique_lock(mutex_type& m);
96 // - unique_lock(mutex_type& m, std::defer_lock_t t) noexcept;
97 // - unique_lock(mutex_type& m, std::try_to_lock_t t);
98 // - unique_lock(mutex_type& m, std::adopt_lock_t t);
99 //
100 // - template <typename Rep, typename Period>
101 // unique_lock(mutex_type& m,
102 // const std::chrono::duration<Rep,Period>& timeout_duration);
103 //
104 // - template<typename Clock, typename Duration>
105 // unique_lock(mutex_type& m,
106 // const std::chrono::time_point<Clock,Duration>& timeout_time);
107 //
108 // - void lock();
109 // - void unlock();
110 // - bool try_lock();
111 //
112 // - template <typename Rep, typename Period>
113 // bool try_lock_for(const std::chrono::duration<Rep,Period>&);
114 //
115 // - template <typename Rep, typename Period>
116 // bool try_lock_until(const std::chrono::time_point<Clock,Duration>&);
117 //
118 // - void swap(unique_lock& other) noexcept;
119 // - mutex_type* release() noexcept;
120 // - mutex_type* mutex() const noexcept;
121 // - bool owns_lock() const noexcept;
122 // - explicit operator bool() const noexcept;
123 // - unique_lock& operator=(unique_lock&& other);
124 //
125 // ---------------------------------------------------------------
126 //
127 // Note that G4AutoLock is defined also for a sequential Geant4 build but below
128 // regarding implementation (also found in G4Threading.hh)
129 //
130 //
131 // NOTE ON GEANT4 SERIAL BUILDS AND MUTEX/UNIQUE_LOCK
132 // ==================================================
133 //
134 // G4Mutex and G4RecursiveMutex are always C++11 std::mutex types
135 // however, in serial mode, using G4MUTEXLOCK and G4MUTEXUNLOCK on these
136 // types has no effect -- i.e. the mutexes are not actually locked or unlocked
137 //
138 // Additionally, when a G4Mutex or G4RecursiveMutex is used with G4AutoLock
139 // and G4RecursiveAutoLock, respectively, these classes also suppressing
140 // the locking and unlocking of the mutex. Regardless of the build type,
141 // G4AutoLock and G4RecursiveAutoLock inherit from std::unique_lock<std::mutex>
142 // and std::unique_lock<std::recursive_mutex>, respectively. This means
143 // that in situations (such as is needed by the analysis category), the
144 // G4AutoLock and G4RecursiveAutoLock can be passed to functions requesting
145 // a std::unique_lock. Within these functions, since std::unique_lock
146 // member functions are not virtual, they will not retain the dummy locking
147 // and unlocking behavior
148 // --> An example of this behavior can be found below
149 //
150 // Jonathan R. Madsen (February 21, 2018)
151 //
262 #ifndef G4AUTOLOCK_HH
263 #define G4AUTOLOCK_HH
264 
265 #include "G4Threading.hh"
266 
267 #include <mutex>
268 #include <chrono>
269 #include <system_error>
270 #include <iostream>
271 
272 // Note: Note that G4TemplateAutoLock by itself is not thread-safe and
273 // cannot be shared among threads due to the locked switch
274 //
275 template <typename _Mutex_t>
276 class G4TemplateAutoLock : public std::unique_lock<_Mutex_t>
277 {
278 public:
279  //------------------------------------------------------------------------//
280  // Some useful typedefs
281  //------------------------------------------------------------------------//
282  typedef std::unique_lock<_Mutex_t> unique_lock_t;
284  typedef typename unique_lock_t::mutex_type mutex_type;
285 
286 public:
287  //------------------------------------------------------------------------//
288  // STL-consistent reference form constructors
289  //------------------------------------------------------------------------//
290 
291  // reference form is consistent with STL lock_guard types
292  // Locks the associated mutex by calling m.lock(). The behavior is
293  // undefined if the current thread already owns the mutex except when
294  // the mutex is recursive
296  : unique_lock_t(_mutex, std::defer_lock)
297  {
298  // call termination-safe locking. if serial, this call has no effect
299  _lock_deferred();
300  }
301 
302  // Tries to lock the associated mutex by calling
303  // m.try_lock_for(_timeout_duration). Blocks until specified
304  // _timeout_duration has elapsed or the lock is acquired, whichever comes
305  // first. May block for longer than _timeout_duration.
306  template <typename Rep, typename Period>
308  const std::chrono::duration<Rep, Period>&
309  _timeout_duration)
310  : unique_lock_t(_mutex, std::defer_lock)
311  {
312  // call termination-safe locking. if serial, this call has no effect
313  _lock_deferred(_timeout_duration);
314  }
315 
316  // Tries to lock the associated mutex by calling
317  // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has
318  // been reached or the lock is acquired, whichever comes first. May block
319  // for longer than until _timeout_time has been reached.
320  template<typename Clock, typename Duration>
322  const std::chrono::time_point<Clock, Duration>&
323  _timeout_time)
324  : unique_lock_t(_mutex, std::defer_lock)
325  {
326  // call termination-safe locking. if serial, this call has no effect
327  _lock_deferred(_timeout_time);
328  }
329 
330  // Does not lock the associated mutex.
331  G4TemplateAutoLock(mutex_type& _mutex, std::defer_lock_t _lock) noexcept
332  : unique_lock_t(_mutex, _lock)
333  { }
334 
335 #ifdef G4MULTITHREADED
336 
337  // Tries to lock the associated mutex without blocking by calling
338  // m.try_lock(). The behavior is undefined if the current thread already
339  // owns the mutex except when the mutex is recursive.
340  G4TemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t _lock)
341  : unique_lock_t(_mutex, _lock)
342  { }
343 
344  // Assumes the calling thread already owns m
345  G4TemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t _lock)
346  : unique_lock_t(_mutex, _lock)
347  { }
348 
349 #else
350 
351  // serial dummy version (initializes unique_lock but does not lock)
352  G4TemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t)
353  : unique_lock_t(_mutex, std::defer_lock)
354  { }
355 
356  // serial dummy version (initializes unique_lock but does not lock)
357  G4TemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t)
358  : unique_lock_t(_mutex, std::defer_lock)
359  { }
360 
361 #endif // defined(G4MULTITHREADED)
362 
363 public:
364  //------------------------------------------------------------------------//
365  // Backwards compatibility versions (constructor with pointer to mutex)
366  //------------------------------------------------------------------------//
368  : unique_lock_t(*_mutex, std::defer_lock)
369  {
370  // call termination-safe locking. if serial, this call has no effect
371  _lock_deferred();
372  }
373 
374  G4TemplateAutoLock(mutex_type* _mutex, std::defer_lock_t _lock) noexcept
375  : unique_lock_t(*_mutex, _lock)
376  { }
377 
378 #if defined(G4MULTITHREADED)
379 
380  G4TemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t _lock)
381  : unique_lock_t(*_mutex, _lock)
382  { }
383 
384  G4TemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t _lock)
385  : unique_lock_t(*_mutex, _lock)
386  { }
387 
388 #else // NOT defined(G4MULTITHREADED) -- i.e. serial
389 
390  G4TemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t)
391  : unique_lock_t(*_mutex, std::defer_lock)
392  { }
393 
394  G4TemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t)
395  : unique_lock_t(*_mutex, std::defer_lock)
396  { }
397 
398 #endif // defined(G4MULTITHREADED)
399 
400 public:
401  //------------------------------------------------------------------------//
402  // Non-constructor overloads
403  //------------------------------------------------------------------------//
404 
405 #if defined(G4MULTITHREADED)
406 
407  // overload nothing
408 
409 #else // NOT defined(G4MULTITHREADED) -- i.e. serial
410 
411  // override unique lock member functions to keep from locking/unlocking
412  // but does not override in polymorphic usage
413  void lock() { }
414  void unlock() { }
415  bool try_lock() { return true; }
416 
417  template <typename Rep, typename Period>
418  bool try_lock_for(const std::chrono::duration<Rep, Period>&)
419  { return true; }
420 
421  template <typename Clock, typename Duration>
422  bool try_lock_until(const std::chrono::time_point<Clock, Duration>&)
423  { return true; }
424 
425  void swap(this_type& other) noexcept { std::swap(*this, other); }
426  bool owns_lock() const noexcept { return false; }
427 
428  // no need to overload
429  //explicit operator bool() const noexcept;
430  //this_type& operator=(this_type&& other);
431  //mutex_type* release() noexcept;
432  //mutex_type* mutex() const noexcept;
433 
434 #endif // defined(G4MULTITHREADED)
435 
436 private:
437  // helpful macros
438  #define _is_stand_mutex(_Tp) (std::is_same<_Tp, G4Mutex>::value)
439  #define _is_recur_mutex(_Tp) (std::is_same<_Tp, G4RecursiveMutex>::value)
440  #define _is_other_mutex(_Tp) (! _is_stand_mutex(_Tp) && ! _is_recur_mutex(_Tp) )
441 
442  template <typename _Tp = _Mutex_t,
443  typename std::enable_if<_is_stand_mutex(_Tp), int>::type = 0>
444  std::string GetTypeString() { return "G4AutoLock<G4Mutex>"; }
445 
446  template <typename _Tp = _Mutex_t,
447  typename std::enable_if<_is_recur_mutex(_Tp), int>::type = 0>
448  std::string GetTypeString() { return "G4AutoLock<G4RecursiveMutex>"; }
449 
450  template <typename _Tp = _Mutex_t,
451  typename std::enable_if<_is_other_mutex(_Tp), int>::type = 0>
452  std::string GetTypeString() { return "G4AutoLock<UNKNOWN_MUTEX>"; }
453 
454  // pollution is bad
455  #undef _is_stand_mutex
456  #undef _is_recur_mutex
457  #undef _is_other_mutex
458 
459  // used in _lock_deferred chrono variants to avoid ununsed-variable warning
460  template <typename _Tp>
461  void suppress_unused_variable(const _Tp&) { }
462 
463  //========================================================================//
464  // NOTE on _lock_deferred(...) variants:
465  // a system_error in lock means that the mutex is unavailable
466  // we want to throw the error that comes from locking an unavailable
467  // mutex so that we know there is a memory leak
468  // if the mutex is valid, this will hold until the other thread
469  // finishes
470 
471  // sometimes certain destructors use locks, this isn't an issue unless
472  // the object is leaked. When this occurs, the application finalization
473  // (i.e. the real or implied "return 0" part of main) will call destructors
474  // on Geant4 object after some static mutex variables are deleted, leading
475  // to the error code (typically on Clang compilers):
476  // libc++abi.dylib: terminating with uncaught exception of type
477  // std::__1::system_error: mutex lock failed: Invalid argument
478  // this function protects against this failure until such a time that
479  // these issues have been resolved
480 
481  //========================================================================//
482  // standard locking
483  inline void _lock_deferred()
484  {
485  #if defined(G4MULTITHREADED)
486  try { this->unique_lock_t::lock(); }
487  catch (std::system_error& e) { PrintLockErrorMessage(e); }
488  #endif
489  }
490 
491  //========================================================================//
492  // Tries to lock the associated mutex by calling
493  // m.try_lock_for(_timeout_duration). Blocks until specified
494  // _timeout_duration has elapsed or the lock is acquired, whichever comes
495  // first. May block for longer than _timeout_duration.
496  template <typename Rep, typename Period>
497  void _lock_deferred(const std::chrono::duration<Rep, Period>&
498  _timeout_duration)
499  {
500  #if defined(G4MULTITHREADED)
501  try { this->unique_lock_t::try_lock_for(_timeout_duration); }
502  catch (std::system_error& e) { PrintLockErrorMessage(e); }
503  #else
504  suppress_unused_variable(_timeout_duration);
505  #endif
506  }
507 
508  //========================================================================//
509  // Tries to lock the associated mutex by calling
510  // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has
511  // been reached or the lock is acquired, whichever comes first. May block
512  // for longer than until _timeout_time has been reached.
513  template<typename Clock, typename Duration>
514  void _lock_deferred(const std::chrono::time_point<Clock, Duration>&
515  _timeout_time)
516  {
517  #if defined(G4MULTITHREADED)
518  try { this->unique_lock_t::try_lock_until(_timeout_time); }
519  catch (std::system_error& e) { PrintLockErrorMessage(e); }
520  #else
521  suppress_unused_variable(_timeout_time);
522  #endif
523  }
524 
525  //========================================================================//
526  // the message for what mutex lock fails due to deleted static mutex
527  // at termination
528  void PrintLockErrorMessage(std::system_error& e)
529  {
530  // use std::cout/std::endl to avoid include dependencies
531  using std::cout;
532  using std::endl;
533  // the error that comes from locking an unavailable mutex
534  #if defined(G4VERBOSE)
535  cout << "Non-critical error: mutex lock failure in "
536  << GetTypeString<mutex_type>() << ". "
537  << "If the app is terminating, Geant4 failed to "
538  << "delete an allocated resource and a Geant4 destructor is "
539  << "being called after the statics were destroyed. \n\t--> "
540  << "Exception: [code: " << e.code() << "] caught: "
541  << e.what() << endl;
542  #else
544  #endif
545  }
546 
547 };
548 
549 // -------------------------------------------------------------------------- //
550 //
551 // Use the non-template types below:
552 // - G4AutoLock with G4Mutex
553 // - G4RecursiveAutoLock with G4RecursiveMutex
554 //
555 // -------------------------------------------------------------------------- //
556 
559 
560 // provide abbriviated type if another mutex type is desired to be used
561 // aside from above
562 template <typename _Tp> using G4TAutoLock = G4TemplateAutoLock<_Tp>;
563 
564 #endif //G4AUTOLOCK_HH
void PrintLockErrorMessage(std::system_error &e)
Definition: G4AutoLock.hh:528
G4TemplateAutoLock(mutex_type &_mutex, std::try_to_lock_t)
Definition: G4AutoLock.hh:352
G4TemplateAutoLock< G4Mutex > G4AutoLock
Definition: G4AutoLock.hh:557
void suppress_unused_variable(const _Tp &)
Definition: G4AutoLock.hh:461
G4TemplateAutoLock< _Mutex_t > this_type
Definition: G4AutoLock.hh:283
void swap(this_type &other) noexcept
Definition: G4AutoLock.hh:425
G4TemplateAutoLock(mutex_type *_mutex, std::adopt_lock_t)
Definition: G4AutoLock.hh:394
void _lock_deferred(const std::chrono::time_point< Clock, Duration > &_timeout_time)
Definition: G4AutoLock.hh:514
G4TemplateAutoLock(mutex_type *_mutex, std::try_to_lock_t)
Definition: G4AutoLock.hh:390
G4TemplateAutoLock(mutex_type *_mutex)
Definition: G4AutoLock.hh:367
bool owns_lock() const noexcept
Definition: G4AutoLock.hh:426
G4TemplateAutoLock(mutex_type &_mutex, const std::chrono::duration< Rep, Period > &_timeout_duration)
Definition: G4AutoLock.hh:307
G4TemplateAutoLock(mutex_type *_mutex, std::defer_lock_t _lock) noexcept
Definition: G4AutoLock.hh:374
bool try_lock_until(const std::chrono::time_point< Clock, Duration > &)
Definition: G4AutoLock.hh:422
unique_lock_t::mutex_type mutex_type
Definition: G4AutoLock.hh:284
G4TemplateAutoLock(mutex_type &_mutex, std::adopt_lock_t)
Definition: G4AutoLock.hh:357
G4TemplateAutoLock(mutex_type &_mutex, std::defer_lock_t _lock) noexcept
Definition: G4AutoLock.hh:331
const G4ThreeVector const G4double const
std::string GetTypeString()
Definition: G4AutoLock.hh:444
G4TemplateAutoLock(mutex_type &_mutex)
Definition: G4AutoLock.hh:295
void _lock_deferred(const std::chrono::duration< Rep, Period > &_timeout_duration)
Definition: G4AutoLock.hh:497
G4TemplateAutoLock< G4RecursiveMutex > G4RecursiveAutoLock
Definition: G4AutoLock.hh:558
bool try_lock_for(const std::chrono::duration< Rep, Period > &)
Definition: G4AutoLock.hh:418
G4TemplateAutoLock(mutex_type &_mutex, const std::chrono::time_point< Clock, Duration > &_timeout_time)
Definition: G4AutoLock.hh:321
std::unique_lock< _Mutex_t > unique_lock_t
Definition: G4AutoLock.hh:282