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.

408 lines
11KB

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