Newton Dynamics  4.00
ndThreadPool.h
1 /* Copyright (c) <2003-2022> <Julio Jerez, Newton Game Dynamics>
2 *
3 * This software is provided 'as-is', without any express or implied
4 * warranty. In no event will the authors be held liable for any damages
5 * arising from the use of this software.
6 *
7 * Permission is granted to anyone to use this software for any purpose,
8 * including commercial applications, and to alter it and redistribute it
9 * freely, subject to the following restrictions:
10 *
11 * 1. The origin of this software must not be misrepresented; you must not
12 * claim that you wrote the original software. If you use this software
13 * in a product, an acknowledgment in the product documentation would be
14 * appreciated but is not required.
15 *
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 *
19 * 3. This notice may not be removed or altered from any source distribution.
20 */
21 
22 #ifndef __ND_THREAD_POOL_H_
23 #define __ND_THREAD_POOL_H_
24 
25 #include "ndCoreStdafx.h"
26 #include "ndTypes.h"
27 #include "ndArray.h"
28 #include "ndThread.h"
29 #include "ndSyncMutex.h"
30 #include "ndSemaphore.h"
31 #include "ndClassAlloc.h"
32 
33 //#define D_MAX_THREADS_COUNT 16
34 #define D_MAX_THREADS_COUNT 32
35 
36 class ndThreadPool;
37 
39 {
40  public:
41  ndStartEnd(ndInt32 count, ndInt32 threadIndex, ndInt32 threads)
42  {
43  ndInt32 stride = count / threads;
44  ndInt32 residual = count - stride * threads;
45  m_start = stride * threadIndex;
46  stride += (threadIndex < residual) ? 1 : 0;
47  m_start += (threadIndex < residual) ? threadIndex : residual;
48  m_end = m_start + stride;
49  }
50 
51  ndInt32 m_start;
52  ndInt32 m_end;
53 };
54 
55 class ndTask
56 {
57  public:
58  ndTask(){}
59  virtual ~ndTask(){}
60  virtual void Execute() const = 0;
61  friend class ndThreadPool;
62 };
63 
64 class ndThreadPool: public ndSyncMutex, public ndThread
65 {
66  class ndWorker: public ndThread
67  {
68  public:
69  D_CORE_API ndWorker();
70  D_CORE_API virtual ~ndWorker();
71 
72  private:
73  virtual void ThreadFunction();
74 
75  ndThreadPool* m_owner;
76  ndAtomic<bool> m_begin;
77  ndAtomic<bool> m_stillLooping;
78  ndAtomic<ndTask*> m_task;
79  ndInt32 m_threadIndex;
80  friend class ndThreadPool;
81  };
82 
83  public:
84  D_CORE_API ndThreadPool(const char* const baseName);
85  D_CORE_API virtual ~ndThreadPool();
86 
87  ndInt32 GetThreadCount() const;
88  D_CORE_API static ndInt32 GetMaxThreads();
89  D_CORE_API void SetThreadCount(ndInt32 count);
90 
91  D_CORE_API void TickOne();
92  D_CORE_API void Begin();
93  D_CORE_API void End();
94 
95  template <typename Function>
96  void ParallelExecute(const Function& ndFunction);
97 
98  private:
99  D_CORE_API virtual void Release();
100 
101  ndWorker* m_workers;
102  ndInt32 m_count;
103  char m_baseName[32];
104 };
105 
106 inline ndInt32 ndThreadPool::GetThreadCount() const
107 {
108  return m_count + 1;
109 }
110 
111 template <typename Type, typename ... Args>
113  :public ndFunction<decltype(&Type::operator())(Args...)>
114 {
115 };
116 
117 template <typename Type>
118 class ndFunction<Type>
119 {
120  public:
121  ndFunction(const Type& obj)
122  :m_object(obj)
123  {
124  }
125 
126  void operator()(ndInt32 threadIndex, ndInt32 threadCount) const
127  {
128  m_object.operator()(threadIndex, threadCount);
129  }
130 
131  private:
132  Type m_object;
133 };
134 
135 namespace ndMakeObject
136 {
137  template<typename Type> auto ndFunction(const Type & obj) -> decltype (::ndFunction<Type>(obj))
138  {
139  return ::ndFunction<Type>(obj);
140  }
141 }
142 
143 template <typename Function>
144 class ndTaskImplement : public ndTask
145 {
146  public:
147  ndTaskImplement(ndInt32 threadIndex, ndThreadPool* const threadPool, const Function& ndFunction)
148  :ndTask()
149  ,m_function(ndFunction)
150  ,m_threadPool(threadPool)
151  ,m_threadIndex(threadIndex)
152  ,m_threadCount(threadPool->GetThreadCount())
153  {
154  }
155 
156  ~ndTaskImplement()
157  {
158  }
159 
160  private:
161  void Execute() const
162  {
163  m_function(m_threadIndex, m_threadCount);
164  }
165 
166  Function m_function;
167  ndThreadPool* m_threadPool;
168  const ndInt32 m_threadIndex;
169  const ndInt32 m_threadCount;
170  friend class ndThreadPool;
171 };
172 
173 template <typename Function>
174 void ndThreadPool::ParallelExecute(const Function& callback)
175 {
176  const ndInt32 threadCount = GetThreadCount();
177  ndTaskImplement<Function>* const jobsArray = ndAlloca(ndTaskImplement<Function>, threadCount);
178 
179  for (ndInt32 i = 0; i < threadCount; ++i)
180  {
181  ndTaskImplement<Function>* const job = &jobsArray[i];
182  new (job) ndTaskImplement<Function>(i, this, callback);
183  }
184 
185  if (m_count > 0)
186  {
187  #ifdef D_USE_THREAD_EMULATION
188  for (ndInt32 i = 0; i < threadCount; ++i)
189  {
190  ndTaskImplement<Function>* const job = &jobsArray[i];
191  callback(job->m_threadIndex, job->m_threadCount);
192  }
193  #else
194  for (ndInt32 i = 0; i < m_count; ++i)
195  {
196  ndTaskImplement<Function>* const job = &jobsArray[i];
197  m_workers[i].m_task.store(job);
198  }
199 
200  ndTaskImplement<Function>* const job = &jobsArray[m_count];
201  callback(job->m_threadIndex, job->m_threadCount);
202 
203  bool jobsInProgress = true;
204  do
205  {
206  ndThreadYield();
207  bool inProgess = false;
208  for (ndInt32 i = 0; i < m_count; ++i)
209  {
210  inProgess = inProgess | (m_workers[i].m_task.load() != nullptr);
211  }
212  jobsInProgress = jobsInProgress & inProgess;
213  } while (jobsInProgress);
214  #endif
215  }
216  else
217  {
218  ndTaskImplement<Function>* const job = &jobsArray[0];
219  callback(job->m_threadIndex, job->m_threadCount);
220  }
221 }
222 
223 #endif
ndFunction
Definition: ndThreadPool.h:114
ndThread
Base class for for all multi thread functionality.
Definition: ndThread.h:49
ndSyncMutex
Generic counting mutex for synchronization of thread jobs.
Definition: ndSyncMutex.h:29
ndTaskImplement
Definition: ndThreadPool.h:145
ndStartEnd
Definition: ndThreadPool.h:39
ndThread::ThreadFunction
virtual void ThreadFunction()=0
Thread function to execute in a perpetual loop until the thread is terminated.
ndFunction< Type >
Definition: ndThreadPool.h:119
ndThreadPool
Definition: ndThreadPool.h:65
ndTask
Definition: ndThreadPool.h:56
ndAtomic< bool >