/src/FreeImage/Source/OpenEXR/IlmThread/IlmThreadPool.cpp

https://bitbucket.org/cabalistic/ogredeps/ · C++ · 456 lines · 231 code · 105 blank · 120 comment · 19 complexity · 701616dd87e9de39df70949f659f20f9 MD5 · raw file

  1. ///////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2005, Industrial Light & Magic, a division of Lucas
  4. // Digital Ltd. LLC
  5. //
  6. // All rights reserved.
  7. //
  8. // Redistribution and use in source and binary forms, with or without
  9. // modification, are permitted provided that the following conditions are
  10. // met:
  11. // * Redistributions of source code must retain the above copyright
  12. // notice, this list of conditions and the following disclaimer.
  13. // * Redistributions in binary form must reproduce the above
  14. // copyright notice, this list of conditions and the following disclaimer
  15. // in the documentation and/or other materials provided with the
  16. // distribution.
  17. // * Neither the name of Industrial Light & Magic nor the names of
  18. // its contributors may be used to endorse or promote products derived
  19. // from this software without specific prior written permission.
  20. //
  21. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. //
  33. ///////////////////////////////////////////////////////////////////////////
  34. //-----------------------------------------------------------------------------
  35. //
  36. // class Task, class ThreadPool, class TaskGroup
  37. //
  38. //-----------------------------------------------------------------------------
  39. #include "IlmThread.h"
  40. #include "IlmThreadMutex.h"
  41. #include "IlmThreadSemaphore.h"
  42. #include "IlmThreadPool.h"
  43. #include "Iex.h"
  44. #include <list>
  45. using namespace std;
  46. namespace IlmThread {
  47. namespace {
  48. class WorkerThread: public Thread
  49. {
  50. public:
  51. WorkerThread (ThreadPool::Data* data);
  52. virtual void run ();
  53. private:
  54. ThreadPool::Data * _data;
  55. };
  56. } //namespace
  57. struct TaskGroup::Data
  58. {
  59. Data ();
  60. ~Data ();
  61. void addTask () ;
  62. void removeTask ();
  63. Semaphore isEmpty; // used to signal that the taskgroup is empty
  64. int numPending; // number of pending tasks to still execute
  65. };
  66. struct ThreadPool::Data
  67. {
  68. Data ();
  69. ~Data();
  70. void finish ();
  71. bool stopped () const;
  72. void stop ();
  73. Semaphore taskSemaphore; // threads wait on this for ready tasks
  74. Mutex taskMutex; // mutual exclusion for the tasks list
  75. list<Task*> tasks; // the list of tasks to execute
  76. size_t numTasks; // fast access to list size
  77. // (list::size() can be O(n))
  78. Semaphore threadSemaphore; // signaled when a thread starts executing
  79. Mutex threadMutex; // mutual exclusion for threads list
  80. list<WorkerThread*> threads; // the list of all threads
  81. size_t numThreads; // fast access to list size
  82. bool stopping; // flag indicating whether to stop threads
  83. Mutex stopMutex; // mutual exclusion for stopping flag
  84. };
  85. //
  86. // class WorkerThread
  87. //
  88. WorkerThread::WorkerThread (ThreadPool::Data* data):
  89. _data (data)
  90. {
  91. start();
  92. }
  93. void
  94. WorkerThread::run ()
  95. {
  96. //
  97. // Signal that the thread has started executing
  98. //
  99. _data->threadSemaphore.post();
  100. while (true)
  101. {
  102. //
  103. // Wait for a task to become available
  104. //
  105. _data->taskSemaphore.wait();
  106. {
  107. Lock taskLock (_data->taskMutex);
  108. //
  109. // If there is a task pending, pop off the next task in the FIFO
  110. //
  111. if (_data->numTasks > 0)
  112. {
  113. Task* task = _data->tasks.front();
  114. TaskGroup* taskGroup = task->group();
  115. _data->tasks.pop_front();
  116. _data->numTasks--;
  117. taskLock.release();
  118. task->execute();
  119. taskLock.acquire();
  120. delete task;
  121. taskGroup->_data->removeTask();
  122. }
  123. else if (_data->stopped())
  124. {
  125. break;
  126. }
  127. }
  128. }
  129. }
  130. //
  131. // struct TaskGroup::Data
  132. //
  133. TaskGroup::Data::Data (): isEmpty (1), numPending (0)
  134. {
  135. // empty
  136. }
  137. TaskGroup::Data::~Data ()
  138. {
  139. //
  140. // A TaskGroup acts like an "inverted" semaphore: if the count
  141. // is above 0 then waiting on the taskgroup will block. This
  142. // destructor waits until the taskgroup is empty before returning.
  143. //
  144. isEmpty.wait ();
  145. }
  146. void
  147. TaskGroup::Data::addTask ()
  148. {
  149. //
  150. // Any access to the taskgroup is protected by a mutex that is
  151. // held by the threadpool. Therefore it is safe to access
  152. // numPending before we wait on the semaphore.
  153. //
  154. if (numPending++ == 0)
  155. isEmpty.wait ();
  156. }
  157. void
  158. TaskGroup::Data::removeTask ()
  159. {
  160. if (--numPending == 0)
  161. isEmpty.post ();
  162. }
  163. //
  164. // struct ThreadPool::Data
  165. //
  166. ThreadPool::Data::Data (): numTasks (0), numThreads (0), stopping (false)
  167. {
  168. // empty
  169. }
  170. ThreadPool::Data::~Data()
  171. {
  172. Lock lock (threadMutex);
  173. finish ();
  174. }
  175. void
  176. ThreadPool::Data::finish ()
  177. {
  178. stop();
  179. //
  180. // Signal enough times to allow all threads to stop.
  181. //
  182. // Wait until all threads have started their run functions.
  183. // If we do not wait before we destroy the threads then it's
  184. // possible that the threads have not yet called their run
  185. // functions.
  186. // If this happens then the run function will be called off
  187. // of an invalid object and we will crash, most likely with
  188. // an error like: "pure virtual method called"
  189. //
  190. for (size_t i = 0; i < numThreads; i++)
  191. {
  192. taskSemaphore.post();
  193. threadSemaphore.wait();
  194. }
  195. //
  196. // Join all the threads
  197. //
  198. for (list<WorkerThread*>::iterator i = threads.begin();
  199. i != threads.end();
  200. ++i)
  201. {
  202. delete (*i);
  203. }
  204. Lock lock1 (taskMutex);
  205. Lock lock2 (stopMutex);
  206. threads.clear();
  207. tasks.clear();
  208. numThreads = 0;
  209. numTasks = 0;
  210. stopping = false;
  211. }
  212. bool
  213. ThreadPool::Data::stopped () const
  214. {
  215. Lock lock (stopMutex);
  216. return stopping;
  217. }
  218. void
  219. ThreadPool::Data::stop ()
  220. {
  221. Lock lock (stopMutex);
  222. stopping = true;
  223. }
  224. //
  225. // class Task
  226. //
  227. Task::Task (TaskGroup* g): _group(g)
  228. {
  229. // empty
  230. }
  231. Task::~Task()
  232. {
  233. // empty
  234. }
  235. TaskGroup*
  236. Task::group ()
  237. {
  238. return _group;
  239. }
  240. TaskGroup::TaskGroup ():
  241. _data (new Data())
  242. {
  243. // empty
  244. }
  245. TaskGroup::~TaskGroup ()
  246. {
  247. delete _data;
  248. }
  249. //
  250. // class ThreadPool
  251. //
  252. ThreadPool::ThreadPool (unsigned nthreads):
  253. _data (new Data())
  254. {
  255. setNumThreads (nthreads);
  256. }
  257. ThreadPool::~ThreadPool ()
  258. {
  259. delete _data;
  260. }
  261. int
  262. ThreadPool::numThreads () const
  263. {
  264. Lock lock (_data->threadMutex);
  265. return _data->numThreads;
  266. }
  267. void
  268. ThreadPool::setNumThreads (int count)
  269. {
  270. if (count < 0)
  271. throw Iex::ArgExc ("Attempt to set the number of threads "
  272. "in a thread pool to a negative value.");
  273. //
  274. // Lock access to thread list and size
  275. //
  276. Lock lock (_data->threadMutex);
  277. if ((size_t)count > _data->numThreads)
  278. {
  279. //
  280. // Add more threads
  281. //
  282. while (_data->numThreads < (size_t)count)
  283. {
  284. _data->threads.push_back (new WorkerThread (_data));
  285. _data->numThreads++;
  286. }
  287. }
  288. else if ((size_t)count < _data->numThreads)
  289. {
  290. //
  291. // Wait until all existing threads are finished processing,
  292. // then delete all threads.
  293. //
  294. _data->finish ();
  295. //
  296. // Add in new threads
  297. //
  298. while (_data->numThreads < (size_t)count)
  299. {
  300. _data->threads.push_back (new WorkerThread (_data));
  301. _data->numThreads++;
  302. }
  303. }
  304. }
  305. void
  306. ThreadPool::addTask (Task* task)
  307. {
  308. //
  309. // Lock the threads, needed to access numThreads
  310. //
  311. Lock lock (_data->threadMutex);
  312. if (_data->numThreads == 0)
  313. {
  314. task->execute ();
  315. delete task;
  316. }
  317. else
  318. {
  319. //
  320. // Get exclusive access to the tasks queue
  321. //
  322. {
  323. Lock taskLock (_data->taskMutex);
  324. //
  325. // Push the new task into the FIFO
  326. //
  327. _data->tasks.push_back (task);
  328. _data->numTasks++;
  329. task->group()->_data->addTask();
  330. }
  331. //
  332. // Signal that we have a new task to process
  333. //
  334. _data->taskSemaphore.post ();
  335. }
  336. }
  337. ThreadPool&
  338. ThreadPool::globalThreadPool ()
  339. {
  340. //
  341. // The global thread pool
  342. //
  343. static ThreadPool gThreadPool (0);
  344. return gThreadPool;
  345. }
  346. void
  347. ThreadPool::addGlobalTask (Task* task)
  348. {
  349. globalThreadPool().addTask (task);
  350. }
  351. } // namespace IlmThread