Nektar++
Thread.h
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File Thread.h
4 //
5 // For more information, please see: http://www.nektar.info
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description:
32 //
33 ///////////////////////////////////////////////////////////////////////////////
34 
35 #ifndef NEKTAR_LIB_UTILITIES_THREAD_H_
36 #define NEKTAR_LIB_UTILITIES_THREAD_H_
37 
38 #include <queue>
39 #include <vector>
40 #include <memory>
41 #include <boost/thread/mutex.hpp>
42 #include <boost/thread/locks.hpp>
43 #include <boost/thread/condition_variable.hpp>
44 #include <boost/thread/thread.hpp>
45 
47 
48 namespace Nektar
49 {
50 namespace Thread
51 {
52 
53 /**
54  * @brief Identifies the algorithm for scheduling.
55  *
56  * Currently there are two strategies to determine the number of jobs.
57  * e_static always loads m_chunkSize jobs onto each worker,
58  * e_guided uses a simple algorithm that loads a larger proportion of jobs
59  * onto the worker as the size of the queue list is large, but scales down
60  * to no fewer than m_chunkSize as the queue empties.
61  *
62  * @see ThreadManager::SetChunkSize()
63  *
64  */
66 {
68  e_dynamic
69 };
70 
71 
72 class ThreadManager;
73 typedef std::shared_ptr<ThreadManager> ThreadManagerSharedPtr;
77 
78 /**
79  * @brief Base class for tasks to be sent to the ThreadManager to run.
80  *
81  * For a parallel region the overall task should be divided into tasklets, each
82  * capable of running independently of other tasklets, in any order. Each of
83  * these tasklets should be represented by an instance of a subclass of
84  * ThreadJob.
85  *
86  * Exactly how to partition the overall task will be problem dependent, but the
87  * ideal case is to have as many tasklets as there are worker threads (available
88  * through ThreadManager::GetNumWorkers() ) and for each tasklet to take the
89  * same amount of computational effort. Clearly, the tasklets should not
90  * overwrite one another's data, and data locations they are writing to should
91  * be sufficiently far apart to avoid cache ping pong.
92  *
93  * Subclasses may define as many variables and functions as are necessary.
94  * Instance of the subclass will be destructed once the ThreadManager has
95  * finished the Run() method.
96  */
97 class ThreadJob
98 {
99  public:
100  /// Base constructor
102  /// Base destructor.
104 
105  /**
106  * This method will be called when the task is loaded
107  * onto a worker thread and is ready to run. When Run
108  * has finished this instance will be destructed.
109  */
110  LIB_UTILITIES_EXPORT virtual void Run() = 0;
111 
112  /// Set number of worker threads.
113  LIB_UTILITIES_EXPORT void SetWorkerNum(unsigned int num);
114 
115  protected:
116  /**
117  * Returns an integer identifying the worker thread the
118  * job is running on. Value will be 0...N, where N is
119  * the number of active worker threads.
120  */
121  unsigned int GetWorkerNum();
122 
123  private:
124  unsigned int m_workerNum;
125 };
126 
127 
128 
129 /**
130  * @brief The interface class for the controller for worker threads and jobs.
131  *
132  * There may be multiple instances of implementations of this class.
133  * They are instantiated by ThreadMaster::CreateInstance(string, int).
134  * Use ThreadMaster::GetInstance(string)
135  * to get a pointer to the instance. Each ThreadManager is associated
136  * in ThreadMaster with a string.
137  *
138  * Jobs are sent to the worker threads by placing them in the
139  * queue with QueueJob() and QueueJobs(). Jobs may be run in
140  * indeterminate order. Jobs are permitted to run *immediately*
141  * they are queued. If this is not desired set the number of active
142  * workers to 0 until all jobs are queued.
143  *
144  * Jobs are taken from the master queue by active worker threads.
145  *
146  * We have the concept of the *master thread*. This is the thread
147  * that instantiates the ThreadManager, calls QueueJob(), Wait()
148  * and so on. This thread must be included in the number of threads
149  * started when the ThreadManager is created, so that no more than
150  * this number of threads are ever executing work at any time. The
151  * master thread is assumed to be working unless it is in Wait().
152  *
153  * Thus if the ThreadManager is called with CreateInstance(string s, int N)
154  * there should be N threads /em including the master thread. If the master
155  * thread calls Wait() (which causes the master thread to sleep until
156  * the job queue is empty) then the master thread should become available
157  * to run jobs from the queue.
158  *
159  * Only the master thread should call ThreadManager methods. Although
160  * it should always be possible for code to identify whether it's the
161  * master thread (through design) there is a InThread() method that returns
162  * true if the thread is a worker.
163  *
164  */
165 class ThreadManager : public std::enable_shared_from_this<ThreadManager>
166 {
167  public:
168  /// Destructor.
170  /**
171  * @brief Pass a list of tasklets to the master queue.
172  * @param joblist Vector of ThreadJob pointers.
173  *
174  * The list of jobs is copied into the master queue. Jobs may be
175  * available for running *immediately*, even before the list has been
176  * fully copied. This can have consequences for the scheduling. If
177  * this is an issue then suspend the workers with SetNumWorkers(0) until
178  * the jobs are queued.
179  *
180  * @see SchedType
181  */
182  LIB_UTILITIES_EXPORT virtual void QueueJobs(std::vector<ThreadJob*>& joblist) = 0;
183  /**
184  * @brief Pass a single job to the master queue.
185  * @param job A pointer to a ThreadJob subclass.
186  *
187  * The job may become available for running immediately. If this is an
188  * issue then suspend the workers with SetNumWorkers(0) until the jobs
189  * are queued.
190  */
191  LIB_UTILITIES_EXPORT virtual void QueueJob(ThreadJob* job) = 0;
192  /**
193  * @brief Return the number of active workers.
194  *
195  * Active workers are threads that are either running jobs
196  * or are waiting for jobs to be queued.
197  */
198  LIB_UTILITIES_EXPORT virtual unsigned int GetNumWorkers() = 0;
199  /**
200  * @brief Returns the worker number of the executing thread.
201  *
202  * Returns an unsigned int between 0 and N-1 where N is the number of
203  * active worker threads. Repeated calls from within this thread will
204  * always return the same value and the value will be the same as
205  * returned from ThreadJob.GetWorkerNum(). The same thread will run a
206  * job until it finishes.
207  *
208  * Although if there are active threads then thread 0 is always one of
209  * them, it is possible that thread 0 does not run for a given set of
210  * jobs. For example, if there are 4 active threads and 3 jobs are
211  * submitted with a e_static scheduling strategy and a chunksize of 1,
212  * then it is possible that threads 1,2, and 3 pick up the jobs and
213  * thread 0 remains idle.
214  *
215  * Returns 0 if called by non-thread.
216  */
217  LIB_UTILITIES_EXPORT virtual unsigned int GetWorkerNum() = 0;
218  /**
219  * @brief Sets the number of active workers.
220  * @param num The number of active workers.
221  *
222  * Active workers are threads that are either running jobs or are
223  * waiting for jobs to be queued.
224  *
225  * If num is greater than the maximum allowed number of active workers,
226  * then the maximum value will be used instead.
227  */
228  LIB_UTILITIES_EXPORT virtual void SetNumWorkers(const unsigned int num) = 0;
229  /**
230  * @brief Sets the number of active workers to the maximum.
231  *
232  * Sets the number of active workers to the maximum available.
233  */
235  /**
236  * @brief Gets the maximum available number of threads.
237  * @return The maximum number of workers.
238  */
239  LIB_UTILITIES_EXPORT virtual unsigned int GetMaxNumWorkers() = 0;
240  /**
241  * @brief Waits until all queued jobs are finished.
242  *
243  * If there are no jobs running or queued this method returns
244  * immediately. Otherwise it blocks until the queue is empty and the
245  * worker threads are idle.
246  *
247  * Implementations *must* ensure that trivial deadlocks are not possible
248  * from this method, that is, that this code:
249  * @code
250  * // assume ThreadManager* tm
251  * // assume SomeJob is subclass of ThreadJob
252  * // assume SomeJob job
253  *
254  * tm->SetNumWorkers(0);
255  * tm->QueueJob(job);
256  * tm->Wait();
257  * @endcode
258  * does not wait forever. Since the master thread is counted in the
259  * number of worker threads, implementations should increase the number
260  * of active workers by 1 on entering Wait().
261  */
262  LIB_UTILITIES_EXPORT virtual void Wait() = 0;
263  /**
264  * @brief Controls how many jobs are sent to each worker at a time.
265  *
266  * The exact meaning of this parameter depends on the current scheduling
267  * algorithm.
268  *
269  * @see SchedType
270  * @see SetSchedType()
271  */
272  LIB_UTILITIES_EXPORT virtual void SetChunkSize(unsigned int chnk) = 0;
273  /**
274  * @brief Sets the current scheduling algorithm.
275  * @see SetChunkSize()
276  */
278  /**
279  * @brief Indicates whether the code is in a worker thread or not.
280  * @return True if the caller is in a worker thread.
281  */
282  LIB_UTILITIES_EXPORT virtual bool InThread() = 0;
283  /**
284  * @brief A calling threads holds until all active threads call this
285  * method.
286  *
287  * When called, the calling thread will sleep until all active workers
288  * have called this method. Once all have done so all threads awake and
289  * continue execution.
290  *
291  * @note Behaviour is likely undefined if the number of active workers
292  * is altered after a thread has called this method. It is only safe to
293  * call SetNumWorkers() when no threads are holding.
294  */
295  LIB_UTILITIES_EXPORT virtual void Hold() = 0;
296  /**
297  * @brief Returns a description of the type of threading.
298  *
299  * E.g. "Threading with Boost"
300  */
301  LIB_UTILITIES_EXPORT virtual const std::string& GetType() const = 0;
302 
303  /// ThreadManager implementation.
304  LIB_UTILITIES_EXPORT virtual bool IsInitialised();
305 
306  inline int GetThrFromPartition(int pPartition)
307  {
308  return pPartition % GetMaxNumWorkers();
309  }
310 
311  inline int GetRankFromPartition(int pPartition)
312  {
313  return pPartition / GetMaxNumWorkers();
314  }
315 
316  inline int GetPartitionFromRankThr(int pRank, unsigned int pThr)
317  {
318  return pRank * GetMaxNumWorkers() + pThr;
319  }
320 };
321 
322 typedef boost::unique_lock<boost::shared_mutex> WriteLock;
323 typedef boost::shared_lock<boost::shared_mutex> ReadLock;
324 
325 /**
326  * A class to manage multiple ThreadManagers. It also acts as a cut-out during
327  * static initialisation, where code attempts to grab a ThreadManager before any
328  * can have been initialised. When this happens the ThreadMaster creates a
329  * simple implementation of ThreadManager, ThreadStartupManager, which behaves
330  * as a single-threaded ThreadManager that fails if anything non-trivial is
331  * attempted. This means code should be cautious about caching the value of
332  * GetInstance(string), as it might change once that particular threading system
333  * is initialised.
334  *
335  * Multiple ThreadManagers should now be possible. Each is identified with a
336  * string and can be recovered by calling
337  * Thread::GetThreadMaster().GetInstance(string).
338  *
339  * ThreadMaster is thread-safe apart from CreateInstance. Call CreateInstance
340  * in single threaded code only.
341  */
343 {
344  private:
345  std::vector<ThreadManagerSharedPtr> m_threadManagers;
346  boost::shared_mutex m_mutex;
347  std::string m_threadingType;
348 
349  public:
353  };
354  /// Constructor
356  /// Destructor
358  /// Sets what ThreadManagers will be created in CreateInstance.
359  LIB_UTILITIES_EXPORT void SetThreadingType(const std::string &p_type);
360  /// Gets the ThreadManager associated with string s.
362  /// Creates an instance of a ThreadManager (which one is determined by
363  /// a previous call to SetThreadingType) and associates it with
364  /// the string s.
366  unsigned int nThr);
367 
368 };
369 LIB_UTILITIES_EXPORT ThreadMaster& GetThreadMaster();
370 
371 
372 /**
373  * @brief A default ThreadManager.
374  *
375  * This will be returned by ThreadMaster if a ThreadManager has not been
376  * initialised, such as if the code is still in static initialisation.
377  *
378  * This manager pretends to be a ThreadManager with 1 thread. It will
379  * cause an error if anything more than trivial functions are called.
380  */
382 {
383  public:
386  virtual ~ThreadStartupManager();
387  virtual void QueueJobs(std::vector<ThreadJob*>& joblist);
388  virtual void QueueJob(ThreadJob* job);
389  virtual unsigned int GetNumWorkers();
390  virtual unsigned int GetWorkerNum();
391  virtual void SetNumWorkers(const unsigned int num);
392  virtual void SetNumWorkers();
393  virtual unsigned int GetMaxNumWorkers();
394  virtual void Wait();
395  virtual void SetChunkSize(unsigned int chnk);
396  virtual void SetSchedType(SchedType s);
397  virtual bool InThread();
398  virtual void Hold();
399  virtual bool IsInitialised();
400  virtual const std::string& GetType() const;
401  private:
402  // Do not allow assignment as m_type is const
404 
405  const std::string m_type;
406 };
407 
408 }
409 
410 }
411 #endif /* THREAD_H_ */
#define LIB_UTILITIES_EXPORT
Provides a generic Factory class.
Definition: NekFactory.hpp:105
Base class for tasks to be sent to the ThreadManager to run.
Definition: Thread.h:98
virtual ~ThreadJob()
Base destructor.
Definition: Thread.cpp:68
void SetWorkerNum(unsigned int num)
Set number of worker threads.
Definition: Thread.cpp:79
virtual void Run()=0
ThreadJob()
Base constructor.
Definition: Thread.cpp:59
unsigned int m_workerNum
Definition: Thread.h:124
unsigned int GetWorkerNum()
Definition: Thread.cpp:88
The interface class for the controller for worker threads and jobs.
Definition: Thread.h:166
virtual void SetNumWorkers(const unsigned int num)=0
Sets the number of active workers.
virtual unsigned int GetWorkerNum()=0
Returns the worker number of the executing thread.
virtual unsigned int GetNumWorkers()=0
Return the number of active workers.
virtual void SetNumWorkers()=0
Sets the number of active workers to the maximum.
virtual void QueueJobs(std::vector< ThreadJob * > &joblist)=0
Pass a list of tasklets to the master queue.
virtual const std::string & GetType() const =0
Returns a description of the type of threading.
int GetPartitionFromRankThr(int pRank, unsigned int pThr)
Definition: Thread.h:316
int GetThrFromPartition(int pPartition)
Definition: Thread.h:306
virtual ~ThreadManager()
Destructor.
Definition: Thread.cpp:112
virtual unsigned int GetMaxNumWorkers()=0
Gets the maximum available number of threads.
virtual void SetChunkSize(unsigned int chnk)=0
Controls how many jobs are sent to each worker at a time.
virtual bool InThread()=0
Indicates whether the code is in a worker thread or not.
int GetRankFromPartition(int pPartition)
Definition: Thread.h:311
virtual void Hold()=0
A calling threads holds until all active threads call this method.
virtual void QueueJob(ThreadJob *job)=0
Pass a single job to the master queue.
virtual bool IsInitialised()
ThreadManager implementation.
Definition: Thread.cpp:100
virtual void SetSchedType(SchedType s)=0
Sets the current scheduling algorithm.
virtual void Wait()=0
Waits until all queued jobs are finished.
~ThreadMaster()
Destructor.
Definition: Thread.cpp:131
void SetThreadingType(const std::string &p_type)
Sets what ThreadManagers will be created in CreateInstance.
Definition: Thread.cpp:157
ThreadManagerSharedPtr CreateInstance(const ThreadManagerName t, unsigned int nThr)
Creates an instance of a ThreadManager (which one is determined by a previous call to SetThreadingTyp...
Definition: Thread.cpp:195
ThreadManagerSharedPtr & GetInstance(const ThreadManagerName t)
Gets the ThreadManager associated with string s.
Definition: Thread.cpp:178
ThreadMaster()
Constructor.
Definition: Thread.cpp:121
std::vector< ThreadManagerSharedPtr > m_threadManagers
Definition: Thread.h:345
boost::shared_mutex m_mutex
Definition: Thread.h:346
std::string m_threadingType
Definition: Thread.h:347
A default ThreadManager.
Definition: Thread.h:382
virtual bool IsInitialised()
ThreadManager implementation.
Definition: Thread.cpp:343
virtual void SetChunkSize(unsigned int chnk)
Controls how many jobs are sent to each worker at a time.
Definition: Thread.cpp:303
virtual void Wait()
Waits until all queued jobs are finished.
Definition: Thread.cpp:294
ThreadStartupManager & operator=(const ThreadStartupManager &src)
ThreadDefaultManager copy constructor.
Definition: Thread.cpp:361
virtual unsigned int GetNumWorkers()
Return the number of active workers.
Definition: Thread.cpp:248
virtual unsigned int GetMaxNumWorkers()
Gets the maximum available number of threads.
Definition: Thread.cpp:285
ThreadStartupManager()
ThreadDefaultManager.
Definition: Thread.cpp:208
virtual void SetNumWorkers()
Sets the number of active workers to the maximum.
Definition: Thread.cpp:276
virtual bool InThread()
Indicates whether the code is in a worker thread or not.
Definition: Thread.cpp:325
ThreadStartupManager(const ThreadStartupManager &src)=default
virtual const std::string & GetType() const
Returns a description of the type of threading.
Definition: Thread.cpp:352
virtual void QueueJobs(std::vector< ThreadJob * > &joblist)
Pass a list of tasklets to the master queue.
Definition: Thread.cpp:226
virtual void SetSchedType(SchedType s)
Sets the current scheduling algorithm.
Definition: Thread.cpp:314
virtual void QueueJob(ThreadJob *job)
Pass a single job to the master queue.
Definition: Thread.cpp:237
virtual unsigned int GetWorkerNum()
Returns the worker number of the executing thread.
Definition: Thread.cpp:257
virtual void Hold()
A calling threads holds until all active threads call this method.
Definition: Thread.cpp:334
SchedType
Identifies the algorithm for scheduling.
Definition: Thread.h:66
LibUtilities::NekFactory< std::string, ThreadManager, unsigned int > ThreadManagerFactory
Definition: Thread.h:75
ThreadManagerFactory & GetThreadManagerFactory()
Definition: Thread.cpp:50
std::shared_ptr< ThreadManager > ThreadManagerSharedPtr
Definition: Thread.h:72
boost::shared_lock< boost::shared_mutex > ReadLock
Definition: Thread.h:323
ThreadMaster & GetThreadMaster()
Definition: Thread.cpp:141
boost::unique_lock< boost::shared_mutex > WriteLock
Definition: Thread.h:322
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:1