VMC Version 2.0
Loading...
Searching...
No Matches
TMCAutoLock.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------
2// Copyright (C) 2019 CERN and copyright holders of VMC Project.
3// This software is distributed under the terms of the GNU General Public
4// License v3 (GPL Version 3), copied verbatim in the file "LICENSE".
5//
6// See https://github.com/vmc-project/vmc for full licensing information.
7// -----------------------------------------------------------------------
8
9// Author: Ivana Hrivnacova, 24/03/2017
10
11/*************************************************************************
12 * Copyright (C) 2014, Rene Brun and Fons Rademakers. *
13 * All rights reserved. *
14 * *
15 * For the licensing terms see $ROOTSYS/LICENSE. *
16 * For the list of contributors see $ROOTSYS/README/CREDITS. *
17 *************************************************************************/
18
19#ifndef TMCAUTOLOCK_HH
20#define TMCAUTOLOCK_HH
21
22//------------------------------------------------
23// The Geant4 Virtual Monte Carlo package
24// Copyright (C) 2013, 2014 Ivana Hrivnacova
25// All rights reserved.
26//
27// For the licensing terms see geant4_vmc/LICENSE.
28// Contact: root-vmc@cern.ch
29//-------------------------------------------------
30
35
36// clang-format off
37
38//
39// ********************************************************************
40// * License and Disclaimer *
41// * *
42// * The Geant4 software is copyright of the Copyright Holders of *
43// * the Geant4 Collaboration. It is provided under the terms and *
44// * conditions of the Geant4 Software License, included in the file *
45// * LICENSE and available at http://cern.ch/geant4/license . These *
46// * include a list of copyright holders. *
47// * *
48// * Neither the authors of this software system, nor their employing *
49// * institutes,nor the agencies providing financial support for this *
50// * work make any representation or warranty, express or implied, *
51// * regarding this software system or assume any liability for its *
52// * use. Please see the license in the file LICENSE and URL above *
53// * for the full disclaimer and the limitation of liability. *
54// * *
55// * This code implementation is the result of the scientific and *
56// * technical work of the GEANT4 collaboration. *
57// * By using, copying, modifying or distributing the software (or *
58// * any work based on the software) you agree to acknowledge its *
59// * use in resulting scientific publications, and indicate your *
60// * acceptance of all terms of the Geant4 Software license. *
61// ********************************************************************
62//
63// G4Autolock
64//
65// Class Description:
66//
67// This class provides a mechanism to create a mutex and locks/unlocks it.
68// Can be used by applications to implement in a portable way a mutexing logic.
69// Usage Example:
70//
71// #include "G4Threading.hh"
72// #include "G4AutoLock.hh"
73//
74// // defined somewhere -- static so all threads see the same mutex
75// static G4Mutex aMutex;
76//
77// // somewhere else:
78// // The G4AutoLock instance will automatically unlock the mutex when it
79// // goes out of scope. One typically defines the scope within { } if
80// // there is thread-safe code following the auto-lock
81//
82// {
83// G4AutoLock l(&aMutex);
84// ProtectedCode();
85// }
86//
87// UnprotectedCode();
88//
89// // When ProtectedCode() is calling a function that also tries to lock
90// // a normal G4AutoLock + G4Mutex will "deadlock". In other words, the
91// // the mutex in the ProtectedCode() function will wait forever to
92// // acquire the lock that is being held by the function that called
93// // ProtectedCode(). In this situation, use a G4RecursiveAutoLock +
94// // G4RecursiveMutex, e.g.
95//
96// // defined somewhere -- static so all threads see the same mutex
97// static G4RecursiveMutex aRecursiveMutex;
98//
99// // this function is sometimes called directly and sometimes called
100// // from SomeFunction_B(), which also locks the mutex
101// void SomeFunction_A()
102// {
103// // when called from SomeFunction_B(), a G4Mutex + G4AutoLock will
104// // deadlock
105// G4RecursiveAutoLock l(&aRecursiveMutex);
106// // do something
107// }
108//
109// void SomeFunction_B()
110// {
111//
112// {
113// G4RecursiveAutoLock l(&aRecursiveMutex);
114// SomeFunction_A();
115// }
116//
117// UnprotectedCode();
118// }
119//
120
121// --------------------------------------------------------------------
122// Author: Andrea Dotti (15 Feb 2013): First Implementation
123//
124// Update: Jonathan Madsen (9 Feb 2018): Replaced custom implementation
125// with inheritance from C++11 unique_lock, which inherits the
126// following member functions:
127//
128// - unique_lock(unique_lock&& other) noexcept;
129// - explicit unique_lock(mutex_type& m);
130// - unique_lock(mutex_type& m, std::defer_lock_t t) noexcept;
131// - unique_lock(mutex_type& m, std::try_to_lock_t t);
132// - unique_lock(mutex_type& m, std::adopt_lock_t t);
133//
134// - template <typename Rep, typename Period>
135// unique_lock(mutex_type& m,
136// const std::chrono::duration<Rep,Period>& timeout_duration);
137//
138// - template<typename Clock, typename Duration>
139// unique_lock(mutex_type& m,
140// const std::chrono::time_point<Clock,Duration>& timeout_time);
141//
142// - void lock();
143// - void unlock();
144// - bool try_lock();
145//
146// - template <typename Rep, typename Period>
147// bool try_lock_for(const std::chrono::duration<Rep,Period>&);
148//
149// - template <typename Rep, typename Period>
150// bool try_lock_until(const std::chrono::time_point<Clock,Duration>&);
151//
152// - void swap(unique_lock& other) noexcept;
153// - mutex_type* release() noexcept;
154// - mutex_type* mutex() const noexcept;
155// - bool owns_lock() const noexcept;
156// - explicit operator bool() const noexcept;
157// - unique_lock& operator=(unique_lock&& other);
158//
159// --------------------------------------------------------------------
160//
161// Note that G4AutoLock is defined also for a sequential Geant4 build but below
162// regarding implementation (also found in G4Threading.hh)
163//
164//
165// NOTE ON GEANT4 SERIAL BUILDS AND MUTEX/UNIQUE_LOCK
166// ==================================================
167//
168// G4Mutex and G4RecursiveMutex are always C++11 std::mutex types
169// however, in serial mode, using G4MUTEXLOCK and G4MUTEXUNLOCK on these
170// types has no effect -- i.e. the mutexes are not actually locked or unlocked
171//
172// Additionally, when a G4Mutex or G4RecursiveMutex is used with G4AutoLock
173// and G4RecursiveAutoLock, respectively, these classes also suppressing
174// the locking and unlocking of the mutex. Regardless of the build type,
175// G4AutoLock and G4RecursiveAutoLock inherit from std::unique_lock<std::mutex>
176// and std::unique_lock<std::recursive_mutex>, respectively. This means
177// that in situations (such as is needed by the analysis category), the
178// G4AutoLock and G4RecursiveAutoLock can be passed to functions requesting
179// a std::unique_lock. Within these functions, since std::unique_lock
180// member functions are not virtual, they will not retain the dummy locking
181// and unlocking behavior
182// --> An example of this behavior can be found below
183//
184// Jonathan R. Madsen (February 21, 2018)
185//
295// --------------------------------------------------------------------
296
297#ifndef _WIN32
298#define TMCMULTITHREADED 1
299#endif
300
301#include <chrono>
302#include <iostream>
303#include <mutex>
304#include <system_error>
305
306
307// Definitions from G4Threading.hh
308
309// Global mutex types
310using TMCMutex = std::mutex;
311using TMCRecursiveMutex = std::recursive_mutex;
313 int (*)(TMCMutex*); // typedef Int (*thread_lock)(TMCMutex*);
315 int (*)(TMCMutex*); // typedef int (*thread_unlock)(TMCMutex*);
316
317
318// Mutex macros
319#define TMCMUTEX_INITIALIZER \
320 {}
321
322#if defined(TMCMULTITHREADED)
323// mutex macros
324#define TMCMUTEXLOCK(mutex) \
325{ \
326 (mutex)->lock(); \
327}
328#define TMCMUTEXUNLOCK(mutex) \
329{ \
330 (mutex)->unlock(); \
331}
332#else
333// mutex macros
334#define TMCMUTEXLOCK(mutex) \
335 {}
336#define TMCMUTEXUNLOCK(mutex) \
337 {}
338#endif
339
340// Note: Note that TMCTemplateAutoLock by itself is not thread-safe and
341// cannot be shared among threads due to the locked switch
342//
343template <typename _Mutex_t>
344class TMCTemplateAutoLock : public std::unique_lock<_Mutex_t>
345{
346 public:
347 //------------------------------------------------------------------------//
348 // Some useful typedefs
349 //------------------------------------------------------------------------//
350 typedef std::unique_lock<_Mutex_t> unique_lock_t;
352 typedef typename unique_lock_t::mutex_type mutex_type;
353
354 public:
355 //------------------------------------------------------------------------//
356 // STL-consistent reference form constructors
357 //------------------------------------------------------------------------//
358
359 // reference form is consistent with STL lock_guard types
360 // Locks the associated mutex by calling m.lock(). The behavior is
361 // undefined if the current thread already owns the mutex except when
362 // the mutex is recursive
364 : unique_lock_t(_mutex, std::defer_lock)
365 {
366 // call termination-safe locking. if serial, this call has no effect
368 }
369
370 // Tries to lock the associated mutex by calling
371 // m.try_lock_for(_timeout_duration). Blocks until specified
372 // _timeout_duration has elapsed or the lock is acquired, whichever comes
373 // first. May block for longer than _timeout_duration.
374 template <typename Rep, typename Period>
376 mutex_type& _mutex,
377 const std::chrono::duration<Rep, Period>& _timeout_duration)
378 : unique_lock_t(_mutex, std::defer_lock)
379 {
380 // call termination-safe locking. if serial, this call has no effect
381 _lock_deferred(_timeout_duration);
382 }
383
384 // Tries to lock the associated mutex by calling
385 // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has
386 // been reached or the lock is acquired, whichever comes first. May block
387 // for longer than until _timeout_time has been reached.
388 template <typename Clock, typename Duration>
390 mutex_type& _mutex,
391 const std::chrono::time_point<Clock, Duration>& _timeout_time)
392 : unique_lock_t(_mutex, std::defer_lock)
393 {
394 // call termination-safe locking. if serial, this call has no effect
395 _lock_deferred(_timeout_time);
396 }
397
398 // Does not lock the associated mutex.
399 TMCTemplateAutoLock(mutex_type& _mutex, std::defer_lock_t _lock) noexcept
400 : unique_lock_t(_mutex, _lock)
401 {}
402
403#ifdef TMCMULTITHREADED
404
405 // Tries to lock the associated mutex without blocking by calling
406 // m.try_lock(). The behavior is undefined if the current thread already
407 // owns the mutex except when the mutex is recursive.
408 TMCTemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t _lock)
409 : unique_lock_t(_mutex, _lock)
410 {}
411
412 // Assumes the calling thread already owns m
413 TMCTemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t _lock)
414 : unique_lock_t(_mutex, _lock)
415 {}
416
417#else
418
419 // serial dummy version (initializes unique_lock but does not lock)
420 TMCTemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t)
421 : unique_lock_t(_mutex, std::defer_lock)
422 {}
423
424 // serial dummy version (initializes unique_lock but does not lock)
425 TMCTemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t)
426 : unique_lock_t(_mutex, std::defer_lock)
427 {}
428
429#endif // defined(TMCMULTITHREADED)
430
431 public:
432 //------------------------------------------------------------------------//
433 // Backwards compatibility versions (constructor with pointer to mutex)
434 //------------------------------------------------------------------------//
436 : unique_lock_t(*_mutex, std::defer_lock)
437 {
438 // call termination-safe locking. if serial, this call has no effect
440 }
441
442 TMCTemplateAutoLock(mutex_type* _mutex, std::defer_lock_t _lock) noexcept
443 : unique_lock_t(*_mutex, _lock)
444 {}
445
446#if defined(TMCMULTITHREADED)
447
448 TMCTemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t _lock)
449 : unique_lock_t(*_mutex, _lock)
450 {}
451
452 TMCTemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t _lock)
453 : unique_lock_t(*_mutex, _lock)
454 {}
455
456#else // NOT defined(TMCMULTITHREADED) -- i.e. serial
457
458 TMCTemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t)
459 : unique_lock_t(*_mutex, std::defer_lock)
460 {}
461
462 TMCTemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t)
463 : unique_lock_t(*_mutex, std::defer_lock)
464 {}
465
466#endif // defined(TMCMULTITHREADED)
467
468 public:
469 //------------------------------------------------------------------------//
470 // Non-constructor overloads
471 //------------------------------------------------------------------------//
472
473#if defined(TMCMULTITHREADED)
474
475 // overload nothing
476
477#else // NOT defined(TMCMULTITHREADED) -- i.e. serial
478
479 // override unique lock member functions to keep from locking/unlocking
480 // but does not override in polymorphic usage
481 void lock() {}
482 void unlock() {}
483 bool try_lock() { return true; }
484
485 template <typename Rep, typename Period>
486 bool try_lock_for(const std::chrono::duration<Rep, Period>&)
487 {
488 return true;
489 }
490
491 template <typename Clock, typename Duration>
492 bool try_lock_until(const std::chrono::time_point<Clock, Duration>&)
493 {
494 return true;
495 }
496
497 void swap(this_type& other) noexcept { std::swap(*this, other); }
498 bool owns_lock() const noexcept { return false; }
499
500 // no need to overload
501 // explicit operator bool() const noexcept;
502 // this_type& operator=(this_type&& other);
503 // mutex_type* release() noexcept;
504 // mutex_type* mutex() const noexcept;
505
506#endif // defined(TMCMULTITHREADED)
507
508 private:
509// helpful macros
510#define _is_stand_mutex(_Tp) (std::is_same<_Tp, TMCMutex>::value)
511#define _is_recur_mutex(_Tp) (std::is_same<_Tp, TMCRecursiveMutex>::value)
512#define _is_other_mutex(_Tp) (!_is_stand_mutex(_Tp) && !_is_recur_mutex(_Tp))
513
514 template <typename _Tp = _Mutex_t,
515 typename std::enable_if<_is_stand_mutex(_Tp), int>::type = 0>
516 std::string GetTypeString()
517 {
518 return "TMCAutoLock<TMCMutex>";
519 }
520
521 template <typename _Tp = _Mutex_t,
522 typename std::enable_if<_is_recur_mutex(_Tp), int>::type = 0>
523 std::string GetTypeString()
524 {
525 return "TMCAutoLock<TMCRecursiveMutex>";
526 }
527
528 template <typename _Tp = _Mutex_t,
529 typename std::enable_if<_is_other_mutex(_Tp), int>::type = 0>
530 std::string GetTypeString()
531 {
532 return "TMCAutoLock<UNKNOWN_MUTEX>";
533 }
534
535// pollution is bad
536#undef _is_stand_mutex
537#undef _is_recur_mutex
538#undef _is_other_mutex
539
540 // used in _lock_deferred chrono variants to avoid ununsed-variable warning
541 template <typename _Tp>
543 {}
544
545 //========================================================================//
546 // NOTE on _lock_deferred(...) variants:
547 // a system_error in lock means that the mutex is unavailable
548 // we want to throw the error that comes from locking an unavailable
549 // mutex so that we know there is a memory leak
550 // if the mutex is valid, this will hold until the other thread
551 // finishes
552
553 // sometimes certain destructors use locks, this isn't an issue unless
554 // the object is leaked. When this occurs, the application finalization
555 // (i.e. the real or implied "return 0" part of main) will call destructors
556 // on Geant4 object after some static mutex variables are deleted, leading
557 // to the error code (typically on Clang compilers):
558 // libc++abi.dylib: terminating with uncaught exception of type
559 // std::__1::system_error: mutex lock failed: Invalid argument
560 // this function protects against this failure until such a time that
561 // these issues have been resolved
562
563 //========================================================================//
564 // standard locking
565 inline void _lock_deferred()
566 {
567#if defined(TMCMULTITHREADED)
568 try
569 {
570 this->unique_lock_t::lock();
571 } catch(std::system_error& e)
572 {
574 }
575#endif
576 }
577
578 //========================================================================//
579 // Tries to lock the associated mutex by calling
580 // m.try_lock_for(_timeout_duration). Blocks until specified
581 // _timeout_duration has elapsed or the lock is acquired, whichever comes
582 // first. May block for longer than _timeout_duration.
583 template <typename Rep, typename Period>
585 const std::chrono::duration<Rep, Period>& _timeout_duration)
586 {
587#if defined(TMCMULTITHREADED)
588 try
589 {
590 this->unique_lock_t::try_lock_for(_timeout_duration);
591 } catch(std::system_error& e)
592 {
594 }
595#else
596 suppress_unused_variable(_timeout_duration);
597#endif
598 }
599
600 //========================================================================//
601 // Tries to lock the associated mutex by calling
602 // m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has
603 // been reached or the lock is acquired, whichever comes first. May block
604 // for longer than until _timeout_time has been reached.
605 template <typename Clock, typename Duration>
607 const std::chrono::time_point<Clock, Duration>& _timeout_time)
608 {
609#if defined(TMCMULTITHREADED)
610 try
611 {
612 this->unique_lock_t::try_lock_until(_timeout_time);
613 } catch(std::system_error& e)
614 {
616 }
617#else
618 suppress_unused_variable(_timeout_time);
619#endif
620 }
621
622 //========================================================================//
623 // the message for what mutex lock fails due to deleted static mutex
624 // at termination
625 void PrintLockErrorMessage(std::system_error& e)
626 {
627 // use std::cout/std::endl to avoid include dependencies
628 using std::cout;
629 using std::endl;
630 // the error that comes from locking an unavailable mutex
631 cout << "Non-critical error: mutex lock failure in "
632 << GetTypeString<mutex_type>() << ". "
633 << "If the app is terminating, Geant4 failed to "
634 << "delete an allocated resource and a Geant4 destructor is "
635 << "being called after the statics were destroyed. \n\t--> "
636 << "Exception: [code: " << e.code() << "] caught: " << e.what()
637 << endl;
638 // suppress_unused_variable(e);
639 }
640};
641
642// -------------------------------------------------------------------------- //
643//
644// Use the non-template types below:
645// - TMCAutoLock with TMCMutex
646// - TMCRecursiveAutoLock with TMCRecursiveMutex
647//
648// -------------------------------------------------------------------------- //
649
652
653// provide abbriviated type if another mutex type is desired to be used
654// aside from above
655template <typename _Tp>
657
658
659
660#endif // TMCAUTOLOCK_HH
#define _is_recur_mutex(_Tp)
Definition: TMCAutoLock.h:511
std::mutex TMCMutex
Definition: TMCAutoLock.h:310
int(*)(TMCMutex *) thread_lock
Definition: TMCAutoLock.h:313
#define _is_stand_mutex(_Tp)
Definition: TMCAutoLock.h:510
int(*)(TMCMutex *) thread_unlock
Definition: TMCAutoLock.h:315
std::recursive_mutex TMCRecursiveMutex
Definition: TMCAutoLock.h:311
#define _is_other_mutex(_Tp)
Definition: TMCAutoLock.h:512
TMCTemplateAutoLock(mutex_type &_mutex)
Definition: TMCAutoLock.h:363
void PrintLockErrorMessage(std::system_error &e)
Definition: TMCAutoLock.h:625
TMCTemplateAutoLock(mutex_type *_mutex, std::defer_lock_t _lock) noexcept
Definition: TMCAutoLock.h:442
std::string GetTypeString()
Definition: TMCAutoLock.h:516
TMCTemplateAutoLock(mutex_type &_mutex, std::adopt_lock_t _lock)
Definition: TMCAutoLock.h:413
void _lock_deferred(const std::chrono::duration< Rep, Period > &_timeout_duration)
Definition: TMCAutoLock.h:584
TMCTemplateAutoLock(mutex_type &_mutex, std::defer_lock_t _lock) noexcept
Definition: TMCAutoLock.h:399
TMCTemplateAutoLock(mutex_type &_mutex, const std::chrono::time_point< Clock, Duration > &_timeout_time)
Definition: TMCAutoLock.h:389
TMCTemplateAutoLock(mutex_type &_mutex, const std::chrono::duration< Rep, Period > &_timeout_duration)
Definition: TMCAutoLock.h:375
void suppress_unused_variable(const _Tp &)
Definition: TMCAutoLock.h:542
TMCTemplateAutoLock(mutex_type *_mutex)
Definition: TMCAutoLock.h:435
void _lock_deferred(const std::chrono::time_point< Clock, Duration > &_timeout_time)
Definition: TMCAutoLock.h:606
TMCTemplateAutoLock(mutex_type *_mutex, std::adopt_lock_t _lock)
Definition: TMCAutoLock.h:452
TMCTemplateAutoLock(mutex_type *_mutex, std::try_to_lock_t _lock)
Definition: TMCAutoLock.h:448
unique_lock_t::mutex_type mutex_type
Definition: TMCAutoLock.h:352
TMCTemplateAutoLock(mutex_type &_mutex, std::try_to_lock_t _lock)
Definition: TMCAutoLock.h:408
TMCTemplateAutoLock< _Mutex_t > this_type
Definition: TMCAutoLock.h:351
std::unique_lock< _Mutex_t > unique_lock_t
Definition: TMCAutoLock.h:350