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