Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
G4AutoLock.hh
Go to the documentation of this file.
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
153void 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
164typedef 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
167void as_unique_lock(unique_lock_t* lock) { lock->lock(); }
168void as_unique_unlock(unique_lock_t* lock) { lock->unlock(); }
169
170//============================================================================//
171
172void 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
193template <typename thread_type = std::thread>
194void 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
245int 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//
272template <typename _Mutex_t>
273class 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>;
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
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>
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>
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 //------------------------------------------------------------------------//
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
584
585// provide abbriviated type if another mutex type is desired to be used
586// aside from above
587template <typename _Tp>
589
590#endif // G4AUTOLOCK_HH
#define _is_recur_mutex(_Tp)
Definition: G4AutoLock.hh:440
#define _is_stand_mutex(_Tp)
Definition: G4AutoLock.hh:439
#define _is_other_mutex(_Tp)
Definition: G4AutoLock.hh:441
G4TemplateAutoLock(mutex_type *_mutex, std::adopt_lock_t)
Definition: G4AutoLock.hh:391
G4TemplateAutoLock(mutex_type &_mutex, std::adopt_lock_t)
Definition: G4AutoLock.hh:354
std::unique_lock< _Mutex_t > unique_lock_t
Definition: G4AutoLock.hh:279
G4TemplateAutoLock(mutex_type *_mutex, std::defer_lock_t _lock) noexcept
Definition: G4AutoLock.hh:371
bool try_lock_until(const std::chrono::time_point< Clock, Duration > &)
Definition: G4AutoLock.hh:421
void swap(this_type &other) noexcept
Definition: G4AutoLock.hh:426
G4TemplateAutoLock(mutex_type &_mutex, std::try_to_lock_t)
Definition: G4AutoLock.hh:349
G4TemplateAutoLock(mutex_type *_mutex, std::try_to_lock_t)
Definition: G4AutoLock.hh:387
G4TemplateAutoLock(mutex_type &_mutex, const std::chrono::duration< Rep, Period > &_timeout_duration)
Definition: G4AutoLock.hh:304
G4TemplateAutoLock(mutex_type &_mutex)
Definition: G4AutoLock.hh:292
bool try_lock_for(const std::chrono::duration< Rep, Period > &)
Definition: G4AutoLock.hh:415
bool owns_lock() const noexcept
Definition: G4AutoLock.hh:427
G4TemplateAutoLock(mutex_type &_mutex, const std::chrono::time_point< Clock, Duration > &_timeout_time)
Definition: G4AutoLock.hh:318
G4TemplateAutoLock(mutex_type *_mutex)
Definition: G4AutoLock.hh:364
typename unique_lock_t::mutex_type mutex_type
Definition: G4AutoLock.hh:281
G4TemplateAutoLock(mutex_type &_mutex, std::defer_lock_t _lock) noexcept
Definition: G4AutoLock.hh:328