The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

420 lines
10KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "../basics/juce_StandardHeader.h"
  24. BEGIN_JUCE_NAMESPACE
  25. #include "juce_ThreadPool.h"
  26. #include "../basics/juce_Time.h"
  27. //==============================================================================
  28. ThreadPoolJob::ThreadPoolJob (const String& name)
  29. : jobName (name),
  30. pool (0),
  31. shouldStop (false),
  32. isActive (false),
  33. shouldBeDeleted (false)
  34. {
  35. }
  36. ThreadPoolJob::~ThreadPoolJob()
  37. {
  38. // you mustn't delete a job while it's still in a pool! Use ThreadPool::removeJob()
  39. // to remove it first!
  40. jassert (pool == 0 || ! pool->contains (this));
  41. }
  42. const String ThreadPoolJob::getJobName() const
  43. {
  44. return jobName;
  45. }
  46. void ThreadPoolJob::setJobName (const String& newName)
  47. {
  48. jobName = newName;
  49. }
  50. void ThreadPoolJob::signalJobShouldExit()
  51. {
  52. shouldStop = true;
  53. }
  54. //==============================================================================
  55. class ThreadPoolThread : public Thread
  56. {
  57. ThreadPool& pool;
  58. bool volatile busy;
  59. ThreadPoolThread (const ThreadPoolThread&);
  60. const ThreadPoolThread& operator= (const ThreadPoolThread&);
  61. public:
  62. ThreadPoolThread (ThreadPool& pool_)
  63. : Thread (T("Pool")),
  64. pool (pool_),
  65. busy (false)
  66. {
  67. }
  68. ~ThreadPoolThread()
  69. {
  70. }
  71. void run()
  72. {
  73. while (! threadShouldExit())
  74. {
  75. if (! pool.runNextJob())
  76. wait (500);
  77. }
  78. }
  79. };
  80. //==============================================================================
  81. ThreadPool::ThreadPool (const int numThreads_,
  82. const bool startThreadsOnlyWhenNeeded,
  83. const int stopThreadsWhenNotUsedTimeoutMs)
  84. : numThreads (jmax (1, numThreads_)),
  85. threadStopTimeout (stopThreadsWhenNotUsedTimeoutMs),
  86. priority (5)
  87. {
  88. jassert (numThreads_ > 0); // not much point having one of these with no threads in it.
  89. threads = (Thread**) juce_calloc (sizeof (Thread*) * numThreads);
  90. for (int i = numThreads; --i >= 0;)
  91. {
  92. threads[i] = new ThreadPoolThread (*this);
  93. if (! startThreadsOnlyWhenNeeded)
  94. threads[i]->startThread (priority);
  95. }
  96. }
  97. ThreadPool::~ThreadPool()
  98. {
  99. removeAllJobs (true, 4000);
  100. int i;
  101. for (i = numThreads; --i >= 0;)
  102. threads[i]->signalThreadShouldExit();
  103. for (i = numThreads; --i >= 0;)
  104. {
  105. threads[i]->stopThread (500);
  106. delete threads[i];
  107. }
  108. juce_free (threads);
  109. }
  110. void ThreadPool::addJob (ThreadPoolJob* const job)
  111. {
  112. jassert (job->pool == 0);
  113. if (job->pool == 0)
  114. {
  115. job->pool = this;
  116. job->shouldStop = false;
  117. job->isActive = false;
  118. lock.enter();
  119. jobs.add (job);
  120. int numRunning = 0;
  121. int i;
  122. for (i = numThreads; --i >= 0;)
  123. if (threads[i]->isThreadRunning() && ! threads[i]->threadShouldExit())
  124. ++numRunning;
  125. if (numRunning < numThreads)
  126. {
  127. bool startedOne = false;
  128. int n = 1000;
  129. while (--n >= 0 && ! startedOne)
  130. {
  131. for (int i = numThreads; --i >= 0;)
  132. {
  133. if (! threads[i]->isThreadRunning())
  134. {
  135. threads[i]->startThread (priority);
  136. startedOne = true;
  137. }
  138. }
  139. if (! startedOne)
  140. Thread::sleep (5);
  141. }
  142. }
  143. lock.exit();
  144. for (i = numThreads; --i >= 0;)
  145. threads[i]->notify();
  146. }
  147. }
  148. int ThreadPool::getNumJobs() const throw()
  149. {
  150. return jobs.size();
  151. }
  152. ThreadPoolJob* ThreadPool::getJob (const int index) const
  153. {
  154. const ScopedLock sl (lock);
  155. return (ThreadPoolJob*) jobs [index];
  156. }
  157. bool ThreadPool::contains (const ThreadPoolJob* const job) const throw()
  158. {
  159. const ScopedLock sl (lock);
  160. return jobs.contains ((void*) job);
  161. }
  162. bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const
  163. {
  164. const ScopedLock sl (lock);
  165. return jobs.contains ((void*) job) && job->isActive;
  166. }
  167. bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job,
  168. const int timeOutMs) const
  169. {
  170. if (job != 0)
  171. {
  172. const uint32 start = Time::getMillisecondCounter();
  173. while (contains (job))
  174. {
  175. if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs)
  176. return false;
  177. Thread::sleep (2);
  178. }
  179. }
  180. return true;
  181. }
  182. bool ThreadPool::removeJob (ThreadPoolJob* const job,
  183. const bool interruptIfRunning,
  184. const int timeOutMs)
  185. {
  186. if (job != 0)
  187. {
  188. lock.enter();
  189. if (jobs.contains (job))
  190. {
  191. if (job->isActive)
  192. {
  193. if (interruptIfRunning)
  194. job->signalJobShouldExit();
  195. lock.exit();
  196. return waitForJobToFinish (job, timeOutMs);
  197. }
  198. else
  199. {
  200. jobs.removeValue (job);
  201. }
  202. }
  203. lock.exit();
  204. }
  205. return true;
  206. }
  207. bool ThreadPool::removeAllJobs (const bool interruptRunningJobs,
  208. const int timeOutMs,
  209. const bool deleteInactiveJobs)
  210. {
  211. lock.enter();
  212. for (int i = jobs.size(); --i >= 0;)
  213. {
  214. ThreadPoolJob* const job = (ThreadPoolJob*) jobs.getUnchecked(i);
  215. if (job->isActive)
  216. {
  217. if (interruptRunningJobs)
  218. job->signalJobShouldExit();
  219. }
  220. else
  221. {
  222. jobs.remove (i);
  223. if (deleteInactiveJobs)
  224. delete job;
  225. }
  226. }
  227. lock.exit();
  228. const uint32 start = Time::getMillisecondCounter();
  229. while (jobs.size() > 0)
  230. {
  231. if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs)
  232. return false;
  233. Thread::sleep (2);
  234. }
  235. return true;
  236. }
  237. const StringArray ThreadPool::getNamesOfAllJobs (const bool onlyReturnActiveJobs) const
  238. {
  239. StringArray s;
  240. const ScopedLock sl (lock);
  241. for (int i = 0; i < jobs.size(); ++i)
  242. {
  243. const ThreadPoolJob* const job = (const ThreadPoolJob*) jobs.getUnchecked(i);
  244. if (job->isActive || ! onlyReturnActiveJobs)
  245. s.add (job->getJobName());
  246. }
  247. return s;
  248. }
  249. bool ThreadPool::setThreadPriorities (const int newPriority)
  250. {
  251. bool ok = true;
  252. if (priority != newPriority)
  253. {
  254. priority = newPriority;
  255. for (int i = numThreads; --i >= 0;)
  256. if (! threads[i]->setPriority (newPriority))
  257. ok = false;
  258. }
  259. return ok;
  260. }
  261. bool ThreadPool::runNextJob()
  262. {
  263. lock.enter();
  264. ThreadPoolJob* job = 0;
  265. for (int i = 0; i < jobs.size(); ++i)
  266. {
  267. job = (ThreadPoolJob*) jobs [i];
  268. if (job != 0 && ! (job->isActive || job->shouldStop))
  269. break;
  270. job = 0;
  271. }
  272. if (job != 0)
  273. {
  274. job->isActive = true;
  275. lock.exit();
  276. JUCE_TRY
  277. {
  278. ThreadPoolJob::JobStatus result = job->runJob();
  279. lastJobEndTime = Time::getApproximateMillisecondCounter();
  280. const ScopedLock sl (lock);
  281. if (jobs.contains (job))
  282. {
  283. job->isActive = false;
  284. if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop)
  285. {
  286. job->pool = 0;
  287. job->shouldStop = true;
  288. jobs.removeValue (job);
  289. if (result == ThreadPoolJob::jobHasFinishedAndShouldBeDeleted)
  290. delete job;
  291. }
  292. else
  293. {
  294. // move the job to the end of the queue if it wants another go
  295. jobs.move (jobs.indexOf (job), -1);
  296. }
  297. }
  298. }
  299. #if JUCE_CATCH_UNHANDLED_EXCEPTIONS
  300. catch (...)
  301. {
  302. lock.enter();
  303. jobs.removeValue (job);
  304. lock.exit();
  305. }
  306. #endif
  307. }
  308. else
  309. {
  310. lock.exit();
  311. if (threadStopTimeout > 0
  312. && Time::getApproximateMillisecondCounter() > lastJobEndTime + threadStopTimeout)
  313. {
  314. lock.enter();
  315. if (jobs.size() == 0)
  316. {
  317. for (int i = numThreads; --i >= 0;)
  318. threads[i]->signalThreadShouldExit();
  319. }
  320. lock.exit();
  321. }
  322. else
  323. {
  324. return false;
  325. }
  326. }
  327. return true;
  328. }
  329. END_JUCE_NAMESPACE