ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4Threading.hh
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file G4Threading.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 //
27 // ---------------------------------------------------------------
28 // GEANT 4 class header file
29 //
30 // Class Description:
31 //
32 // This file defines types and macros used to expose Geant4 threading model.
33 
34 // ---------------------------------------------------------------
35 // Author: Andrea Dotti (15 Feb 2013): First Implementation
36 // ---------------------------------------------------------------
37 #ifndef G4Threading_hh
38 #define G4Threading_hh
39 
40 #include "globals.hh"
41 #include "G4Types.hh"
42 
43 #include <chrono>
44 #include <thread>
45 #include <mutex>
46 #include <condition_variable>
47 #include <future>
48 #include <vector>
49 
50 // Macro to put current thread to sleep
51 //
52 #define G4THREADSLEEP(tick) \
53  std::this_thread::sleep_for(std::chrono::seconds( tick ))
54 
55 // will be used in the future when migrating threading to task-based style
56 template <typename _Tp> using G4Future = std::future<_Tp>;
57 template <typename _Tp> using G4SharedFuture = std::shared_future<_Tp>;
58 template <typename _Tp> using G4Promise = std::promise<_Tp>;
59 
60 //
61 // NOTE ON GEANT4 SERIAL BUILDS AND MUTEX/UNIQUE_LOCK
62 // ==================================================
63 //
64 // G4Mutex and G4RecursiveMutex are always C++11 std::mutex types
65 // however, in serial mode, using G4MUTEXLOCK and G4MUTEXUNLOCK on these
66 // types has no effect -- i.e. the mutexes are not actually locked or unlocked
67 //
68 // Additionally, when a G4Mutex or G4RecursiveMutex is used with G4AutoLock
69 // and G4RecursiveAutoLock, respectively, these classes also suppressing
70 // the locking and unlocking of the mutex. Regardless of the build type,
71 // G4AutoLock and G4RecursiveAutoLock inherit from std::unique_lock<std::mutex>
72 // and std::unique_lock<std::recursive_mutex>, respectively. This means
73 // that in situations (such as is needed by the analysis category), the
74 // G4AutoLock and G4RecursiveAutoLock can be passed to functions requesting
75 // a std::unique_lock. Within these functions, since std::unique_lock
76 // member functions are not virtual, they will not retain the dummy locking
77 // and unlocking behavior
78 // --> An example of this behavior can be found in G4AutoLock.hh
79 //
80 // Jonathan R. Madsen (February 21, 2018)
81 //
82 
83 // global mutex types
85 using G4RecursiveMutex = std::recursive_mutex;
86 
87 // mutex macros
88 #define G4MUTEX_INITIALIZER {}
89 #define G4MUTEXINIT(mutex) ;;
90 #define G4MUTEXDESTROY(mutex) ;;
91 
92 // static functions: get_id(), sleep_for(...), sleep_until(...), yield(),
93 namespace G4ThisThread { using namespace std::this_thread; }
94 
95 // will be used in the future when migrating threading to task-based style
96 // and are currently used in unit tests
97 template <typename _Tp> using G4Promise = std::promise<_Tp>;
98 template <typename _Tp> using G4Future = std::future<_Tp>;
99 template <typename _Tp> using G4SharedFuture = std::shared_future<_Tp>;
100 
101 // Some useful types
102 using G4ThreadFunReturnType = void*;
103 using G4ThreadFunArgType = void*;
104 using thread_lock = G4int(*)(G4Mutex*); // typedef G4int (*thread_lock)(G4Mutex*);
105 using thread_unlock = G4int(*)(G4Mutex*); // typedef G4int (*thread_unlock)(G4Mutex*);
106 
107 // Helper function for getting a unique static mutex for a specific
108 // class or type
109 // Usage example:
110 // a template class "G4Cache<T>" that required a static
111 // mutex for specific to type T:
112 // G4AutoLock l(G4TypeMutex<G4Cache<T>>());
113 template <typename _Tp>
114 G4Mutex& G4TypeMutex(const unsigned int& _n = 0)
115 {
116  static G4Mutex* _mutex = new G4Mutex();
117  if(_n == 0)
118  return *_mutex;
119 
120  static std::vector<G4Mutex*> _mutexes;
121  if(_n > _mutexes.size())
122  _mutexes.resize(_n, nullptr);
123  if(!_mutexes[_n])
124  _mutexes[_n] = new G4Mutex();
125  return *(_mutexes[_n-1]);
126 }
127 
128 // Helper function for getting a unique static recursive_mutex for a
129 // specific class or type
130 // Usage example:
131 // a template class "G4Cache<T>" that required a static
132 // recursive_mutex for specific to type T:
133 // G4RecursiveAutoLock l(G4TypeRecursiveMutex<G4Cache<T>>());
134 template <typename _Tp>
135 G4RecursiveMutex& G4TypeRecursiveMutex(const unsigned int& _n = 0)
136 {
137  static G4RecursiveMutex* _mutex = new G4RecursiveMutex();
138  if(_n == 0)
139  return *(_mutex);
140 
141  static std::vector<G4RecursiveMutex*> _mutexes;
142  if(_n > _mutexes.size())
143  _mutexes.resize(_n, nullptr);
144  if(!_mutexes[_n])
145  _mutexes[_n] = new G4RecursiveMutex();
146  return *(_mutexes[_n-1]);
147 }
148 
149 #if defined(G4MULTITHREADED)
150  //==========================================
151  // G4MULTITHREADED is ON - threading enabled
152  //==========================================
153 
154  // global thread types
155  using G4Thread = std::thread;
156  using G4NativeThread = std::thread::native_handle_type;
157 
158  // mutex macros
159  #define G4MUTEXLOCK(mutex) { (mutex)->lock(); }
160  #define G4MUTEXUNLOCK(mutex) { (mutex)->unlock(); }
161 
162  // Macro to join thread
163  #define G4THREADJOIN(worker) (worker).join()
164 
165  // std::thread::id does not cast to integer
166  using G4Pid_t = std::thread::id;
167 
168  // Instead of previous macro taking one argument, define function taking
169  // unlimited arguments
170  template <typename _Worker, typename _Func, typename... _Args>
171  void G4THREADCREATE(_Worker*& worker, _Func func, _Args... args)
172  {
173  *worker = G4Thread(func, std::forward<_Args>(args)...);
174  }
175 
176  // Conditions
177  //
178  // See G4MTRunManager for example on how to use these
179  //
180  using G4Condition = std::condition_variable;
181  #define G4CONDITION_INITIALIZER {}
182  #define G4CONDITIONWAIT(cond, lock) (cond)->wait(*lock);
183  #define G4CONDITIONWAITLAMBDA(cond, lock, lambda) (cond)->wait(*lock, lambda);
184  #define G4CONDITIONNOTIFY(cond) (cond)->notify_one();
185  #define G4CONDITIONBROADCAST(cond) (cond)->notify_all();
186  //
187  // we don't define above globally so single-threaded code does not get
188  // caught in condition with no other thread to wake it up
189  //
190 
191 #else
192  //==========================================
193  // G4MULTITHREADED is OFF - Sequential build
194  //==========================================
195 
196  // implement a dummy thread class that acts like a thread
198  {
199  public:
201  using id = std::thread::id;
202 
203  public:
204  // does nothing
206  { }
207  // a std::thread-like constructor that execute upon construction
208  template <typename _Func, typename... _Args>
209  G4DummyThread(_Func func, _Args&&... _args)
210  {
211  func(std::forward<_Args>(_args)...);
212  }
213 
214  public:
216  bool joinable() const { return true; }
217  id get_id() const noexcept { return std::this_thread::get_id(); }
218  void swap(G4DummyThread&) { }
219  void join() { }
220  void detach() { }
221 
222  public:
223  static unsigned int hardware_concurrency() noexcept
224  {
225  return std::thread::hardware_concurrency();
226  }
227  };
228 
229  // global thread types
232 
233  // mutex macros
234  #define G4MUTEXLOCK(mutex) ;;
235  #define G4MUTEXUNLOCK(mutex) ;;
236 
237  // Macro to join thread
238  #define G4THREADJOIN(worker) ;;
239 
240  using G4Pid_t = G4int;
241 
242  // Instead of previous macro taking one argument, define function taking
243  // unlimited arguments
244  template <typename _Worker, typename _Func, typename... _Args>
245  void G4THREADCREATE(_Worker*& worker, _Func func, _Args... args)
246  {
247  *worker = G4Thread(func, std::forward<_Args>(args)...);
248  }
249 
251  #define G4CONDITION_INITIALIZER 1
252  #define G4CONDITIONWAIT(cond, mutex) G4ConsumeParameters(cond, mutex);
253  #define G4CONDITIONWAITLAMBDA(cond, mutex, lambda) G4ConsumeParameters(cond, mutex, lambda);
254  #define G4CONDITIONNOTIFY(cond) G4ConsumeParameters(cond);
255  #define G4CONDITIONBROADCAST(cond) G4ConsumeParameters(cond);
256 
257 #endif //G4MULTITHREADING
258 
259 //============================================================================//
260 
261 // Define here after G4Thread has been typedef
263 
264 //============================================================================//
265 
266 namespace G4Threading
267 {
268  enum
269  {
271  MASTER_ID = -1,
274  };
275 
281  void G4SetThreadId( G4int aNewValue );
286  int WorkerThreadJoinsPool();
288 }
289 
290 #endif //G4Threading_hh