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.

340 lines
8.7KB

  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. #include "../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_Thread.h"
  21. #include "juce_SpinLock.h"
  22. #include "../core/juce_Time.h"
  23. //==============================================================================
  24. class RunningThreadsList
  25. {
  26. public:
  27. RunningThreadsList()
  28. {
  29. }
  30. void add (Thread* const thread)
  31. {
  32. const SpinLock::ScopedLockType sl (lock);
  33. jassert (! threads.contains (thread));
  34. threads.add (thread);
  35. }
  36. void remove (Thread* const thread)
  37. {
  38. const SpinLock::ScopedLockType sl (lock);
  39. jassert (threads.contains (thread));
  40. threads.removeValue (thread);
  41. }
  42. int size() const noexcept
  43. {
  44. return threads.size();
  45. }
  46. Thread* getThreadWithID (const Thread::ThreadID targetID) const noexcept
  47. {
  48. const SpinLock::ScopedLockType sl (lock);
  49. for (int i = threads.size(); --i >= 0;)
  50. {
  51. Thread* const t = threads.getUnchecked(i);
  52. if (t->getThreadId() == targetID)
  53. return t;
  54. }
  55. return nullptr;
  56. }
  57. void stopAll (const int timeOutMilliseconds)
  58. {
  59. signalAllThreadsToStop();
  60. for (;;)
  61. {
  62. Thread* firstThread = getFirstThread();
  63. if (firstThread != nullptr)
  64. firstThread->stopThread (timeOutMilliseconds);
  65. else
  66. break;
  67. }
  68. }
  69. static RunningThreadsList& getInstance()
  70. {
  71. static RunningThreadsList runningThreads;
  72. return runningThreads;
  73. }
  74. private:
  75. Array<Thread*> threads;
  76. SpinLock lock;
  77. void signalAllThreadsToStop()
  78. {
  79. const SpinLock::ScopedLockType sl (lock);
  80. for (int i = threads.size(); --i >= 0;)
  81. threads.getUnchecked(i)->signalThreadShouldExit();
  82. }
  83. Thread* getFirstThread() const
  84. {
  85. const SpinLock::ScopedLockType sl (lock);
  86. return threads.getFirst();
  87. }
  88. };
  89. //==============================================================================
  90. void Thread::threadEntryPoint()
  91. {
  92. RunningThreadsList::getInstance().add (this);
  93. JUCE_TRY
  94. {
  95. if (threadName_.isNotEmpty())
  96. setCurrentThreadName (threadName_);
  97. if (startSuspensionEvent_.wait (10000))
  98. {
  99. jassert (getCurrentThreadId() == threadId_);
  100. if (affinityMask_ != 0)
  101. setCurrentThreadAffinityMask (affinityMask_);
  102. run();
  103. }
  104. }
  105. JUCE_CATCH_ALL_ASSERT
  106. RunningThreadsList::getInstance().remove (this);
  107. closeThreadHandle();
  108. }
  109. // used to wrap the incoming call from the platform-specific code
  110. void JUCE_API juce_threadEntryPoint (void* userData)
  111. {
  112. static_cast <Thread*> (userData)->threadEntryPoint();
  113. }
  114. //==============================================================================
  115. Thread::Thread (const String& threadName)
  116. : threadName_ (threadName),
  117. threadHandle_ (nullptr),
  118. threadId_ (0),
  119. threadPriority_ (5),
  120. affinityMask_ (0),
  121. threadShouldExit_ (false)
  122. {
  123. }
  124. Thread::~Thread()
  125. {
  126. /* If your thread class's destructor has been called without first stopping the thread, that
  127. means that this partially destructed object is still performing some work - and that's
  128. probably a Bad Thing!
  129. To avoid this type of nastiness, always make sure you call stopThread() before or during
  130. your subclass's destructor.
  131. */
  132. jassert (! isThreadRunning());
  133. stopThread (100);
  134. }
  135. //==============================================================================
  136. void Thread::startThread()
  137. {
  138. const ScopedLock sl (startStopLock);
  139. threadShouldExit_ = false;
  140. if (threadHandle_ == nullptr)
  141. {
  142. launchThread();
  143. setThreadPriority (threadHandle_, threadPriority_);
  144. startSuspensionEvent_.signal();
  145. }
  146. }
  147. void Thread::startThread (const int priority)
  148. {
  149. const ScopedLock sl (startStopLock);
  150. if (threadHandle_ == nullptr)
  151. {
  152. threadPriority_ = priority;
  153. startThread();
  154. }
  155. else
  156. {
  157. setPriority (priority);
  158. }
  159. }
  160. bool Thread::isThreadRunning() const
  161. {
  162. return threadHandle_ != nullptr;
  163. }
  164. //==============================================================================
  165. void Thread::signalThreadShouldExit()
  166. {
  167. threadShouldExit_ = true;
  168. }
  169. bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const
  170. {
  171. // Doh! So how exactly do you expect this thread to wait for itself to stop??
  172. jassert (getThreadId() != getCurrentThreadId());
  173. const int sleepMsPerIteration = 5;
  174. int count = timeOutMilliseconds / sleepMsPerIteration;
  175. while (isThreadRunning())
  176. {
  177. if (timeOutMilliseconds > 0 && --count < 0)
  178. return false;
  179. sleep (sleepMsPerIteration);
  180. }
  181. return true;
  182. }
  183. void Thread::stopThread (const int timeOutMilliseconds)
  184. {
  185. // agh! You can't stop the thread that's calling this method! How on earth
  186. // would that work??
  187. jassert (getCurrentThreadId() != getThreadId());
  188. const ScopedLock sl (startStopLock);
  189. if (isThreadRunning())
  190. {
  191. signalThreadShouldExit();
  192. notify();
  193. if (timeOutMilliseconds != 0)
  194. waitForThreadToExit (timeOutMilliseconds);
  195. if (isThreadRunning())
  196. {
  197. // very bad karma if this point is reached, as there are bound to be
  198. // locks and events left in silly states when a thread is killed by force..
  199. jassertfalse;
  200. Logger::writeToLog ("!! killing thread by force !!");
  201. killThread();
  202. RunningThreadsList::getInstance().remove (this);
  203. threadHandle_ = nullptr;
  204. threadId_ = 0;
  205. }
  206. }
  207. }
  208. //==============================================================================
  209. bool Thread::setPriority (const int priority)
  210. {
  211. const ScopedLock sl (startStopLock);
  212. if (setThreadPriority (threadHandle_, priority))
  213. {
  214. threadPriority_ = priority;
  215. return true;
  216. }
  217. return false;
  218. }
  219. bool Thread::setCurrentThreadPriority (const int priority)
  220. {
  221. return setThreadPriority (0, priority);
  222. }
  223. void Thread::setAffinityMask (const uint32 affinityMask)
  224. {
  225. affinityMask_ = affinityMask;
  226. }
  227. //==============================================================================
  228. bool Thread::wait (const int timeOutMilliseconds) const
  229. {
  230. return defaultEvent_.wait (timeOutMilliseconds);
  231. }
  232. void Thread::notify() const
  233. {
  234. defaultEvent_.signal();
  235. }
  236. //==============================================================================
  237. int Thread::getNumRunningThreads()
  238. {
  239. return RunningThreadsList::getInstance().size();
  240. }
  241. Thread* Thread::getCurrentThread()
  242. {
  243. return RunningThreadsList::getInstance().getThreadWithID (getCurrentThreadId());
  244. }
  245. void Thread::stopAllThreads (const int timeOutMilliseconds)
  246. {
  247. RunningThreadsList::getInstance().stopAll (timeOutMilliseconds);
  248. }
  249. //==============================================================================
  250. void SpinLock::enter() const noexcept
  251. {
  252. if (! lock.compareAndSetBool (1, 0))
  253. {
  254. for (int i = 20; --i >= 0;)
  255. if (lock.compareAndSetBool (1, 0))
  256. return;
  257. while (! lock.compareAndSetBool (1, 0))
  258. Thread::yield();
  259. }
  260. }
  261. bool SpinLock::tryEnter() const noexcept
  262. {
  263. return lock.compareAndSetBool (1, 0);
  264. }
  265. END_JUCE_NAMESPACE