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.

351 lines
9.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../jucedemo_headers.h"
  18. //==============================================================================
  19. class BouncingBallComp : public Component
  20. {
  21. public:
  22. BouncingBallComp()
  23. {
  24. x = Random::getSystemRandom().nextFloat() * 200.0f;
  25. y = Random::getSystemRandom().nextFloat() * 200.0f;
  26. parentWidth = 50;
  27. parentHeight = 50;
  28. innerX = 0;
  29. innerY = 0;
  30. threadId = 0;
  31. const float speed = 5.0f; // give each ball a fixed speed so we can
  32. // see the effects of thread priority on how fast
  33. // they actually go.
  34. const float angle = Random::getSystemRandom().nextFloat() * float_Pi * 2.0f;
  35. dx = sinf (angle) * speed;
  36. dy = cosf (angle) * speed;
  37. size = Random::getSystemRandom().nextFloat() * 30.0f + 30.0f;
  38. colour = Colour (Random::getSystemRandom().nextInt())
  39. .withAlpha (0.5f)
  40. .withBrightness (0.7f);
  41. }
  42. ~BouncingBallComp()
  43. {
  44. }
  45. void paint (Graphics& g)
  46. {
  47. g.setColour (colour);
  48. g.fillEllipse (innerX, innerY, size, size);
  49. g.setColour (Colours::black);
  50. g.setFont (10.0f);
  51. g.drawText (String::toHexString ((int64) threadId), getLocalBounds(), Justification::centred, false);
  52. }
  53. void parentSizeChanged()
  54. {
  55. parentWidth = getParentWidth() - size;
  56. parentHeight = getParentHeight() - size;
  57. }
  58. void moveBall()
  59. {
  60. threadId = Thread::getCurrentThreadId(); // this is so the component can print the thread ID inside the ball
  61. x += dx;
  62. y += dy;
  63. if (x < 0)
  64. dx = fabsf (dx);
  65. if (x > parentWidth)
  66. dx = -fabsf (dx);
  67. if (y < 0)
  68. dy = fabsf (dy);
  69. if (y > parentHeight)
  70. dy = -fabsf (dy);
  71. setBounds (((int) x) - 2,
  72. ((int) y) - 2,
  73. ((int) size) + 4,
  74. ((int) size) + 4);
  75. innerX = x - getX();
  76. innerY = y - getY();
  77. repaint();
  78. }
  79. private:
  80. float x, y, size, dx, dy, w, h, parentWidth, parentHeight;
  81. float innerX, innerY;
  82. Colour colour;
  83. Thread::ThreadID threadId;
  84. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallComp)
  85. };
  86. //==============================================================================
  87. class DemoThread : public BouncingBallComp,
  88. public Thread
  89. {
  90. public:
  91. DemoThread()
  92. : Thread ("Juce Demo Thread")
  93. {
  94. interval = Random::getSystemRandom().nextInt (50) + 6;
  95. // give the threads a random priority, so some will move more
  96. // smoothly than others..
  97. startThread (Random::getSystemRandom().nextInt (3) + 3);
  98. }
  99. ~DemoThread()
  100. {
  101. // allow the thread 2 seconds to stop cleanly - should be plenty of time.
  102. stopThread (2000);
  103. }
  104. void run() override
  105. {
  106. // this is the code that runs this thread - we'll loop continuously,
  107. // updating the co-ordinates of our blob.
  108. // threadShouldExit() returns true when the stopThread() method has been
  109. // called, so we should check it often, and exit as soon as it gets flagged.
  110. while (! threadShouldExit())
  111. {
  112. // sleep a bit so the threads don't all grind the CPU to a halt..
  113. wait (interval);
  114. // because this is a background thread, we mustn't do any UI work without
  115. // first grabbing a MessageManagerLock..
  116. const MessageManagerLock mml (Thread::getCurrentThread());
  117. if (! mml.lockWasGained()) // if something is trying to kill this job, the lock
  118. return; // will fail, in which case we'd better return..
  119. // now we've got the UI thread locked, we can mess about with the components
  120. moveBall();
  121. }
  122. }
  123. private:
  124. int interval;
  125. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThread)
  126. };
  127. //==============================================================================
  128. class DemoThreadPoolJob : public BouncingBallComp,
  129. public ThreadPoolJob
  130. {
  131. public:
  132. DemoThreadPoolJob()
  133. : ThreadPoolJob ("Demo Threadpool Job")
  134. {
  135. }
  136. JobStatus runJob()
  137. {
  138. // this is the code that runs this job. It'll be repeatedly called until we return
  139. // jobHasFinished instead of jobNeedsRunningAgain.
  140. Thread::sleep (30);
  141. // because this is a background thread, we mustn't do any UI work without
  142. // first grabbing a MessageManagerLock..
  143. const MessageManagerLock mml (this);
  144. // before moving the ball, we need to check whether the lock was actually gained, because
  145. // if something is trying to stop this job, it will have failed..
  146. if (mml.lockWasGained())
  147. moveBall();
  148. return jobNeedsRunningAgain;
  149. }
  150. void removedFromQueue()
  151. {
  152. // This is called to tell us that our job has been removed from the pool.
  153. // In this case there's no need to do anything here.
  154. }
  155. private:
  156. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThreadPoolJob)
  157. };
  158. //==============================================================================
  159. class ThreadingDemo : public Component,
  160. public Timer,
  161. public ButtonListener
  162. {
  163. public:
  164. //==============================================================================
  165. ThreadingDemo()
  166. : pool (3),
  167. controlButton ("Thread type"),
  168. isUsingPool (false)
  169. {
  170. setName ("Multithreading");
  171. setOpaque (true);
  172. addAndMakeVisible (&controlButton);
  173. controlButton.changeWidthToFitText (20);
  174. controlButton.setTopLeftPosition (20, 20);
  175. controlButton.setTriggeredOnMouseDown (true);
  176. controlButton.setAlwaysOnTop (true);
  177. controlButton.addListener (this);
  178. }
  179. ~ThreadingDemo()
  180. {
  181. pool.removeAllJobs (true, 2000);
  182. }
  183. void resetAllBalls()
  184. {
  185. stopTimer();
  186. pool.removeAllJobs (true, 4000);
  187. balls.clear();
  188. if (isShowing())
  189. {
  190. while (balls.size() < 5)
  191. addABall();
  192. startTimer (300);
  193. }
  194. }
  195. void paint (Graphics& g)
  196. {
  197. g.fillAll (Colours::white);
  198. }
  199. void setUsingPool (bool usePool)
  200. {
  201. isUsingPool = usePool;
  202. resetAllBalls();
  203. }
  204. void addABall()
  205. {
  206. if (isUsingPool)
  207. {
  208. DemoThreadPoolJob* newBall = new DemoThreadPoolJob();
  209. balls.add (newBall);
  210. addAndMakeVisible (newBall);
  211. newBall->parentSizeChanged();
  212. pool.addJob (newBall, false);
  213. }
  214. else
  215. {
  216. DemoThread* newBall = new DemoThread();
  217. balls.add (newBall);
  218. addAndMakeVisible (newBall);
  219. newBall->parentSizeChanged();
  220. }
  221. }
  222. void removeABall()
  223. {
  224. if (balls.size() > 0)
  225. {
  226. int indexToRemove = Random::getSystemRandom().nextInt (balls.size());
  227. if (isUsingPool)
  228. pool.removeJob (dynamic_cast <DemoThreadPoolJob*> (balls [indexToRemove]), true, 4000);
  229. balls.remove (indexToRemove);
  230. }
  231. }
  232. void timerCallback()
  233. {
  234. if (Random::getSystemRandom().nextBool())
  235. {
  236. if (balls.size() <= 10)
  237. addABall();
  238. }
  239. else
  240. {
  241. if (balls.size() > 3)
  242. removeABall();
  243. }
  244. }
  245. void buttonClicked (Button*)
  246. {
  247. PopupMenu m;
  248. m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
  249. m.addItem (2, "Use a thread pool", true, isUsingPool);
  250. m.showMenuAsync (PopupMenu::Options().withTargetComponent (&controlButton),
  251. ModalCallbackFunction::forComponent (menuItemChosenCallback, this));
  252. }
  253. static void menuItemChosenCallback (int result, ThreadingDemo* demoComponent)
  254. {
  255. if (demoComponent != 0)
  256. demoComponent->setUsingPool (result == 2);
  257. }
  258. // this gets called when a component is added or removed from a parent component.
  259. void parentHierarchyChanged()
  260. {
  261. // we'll use this as an opportunity to start and stop the threads, so that
  262. // we don't leave them going when the component's not actually visible.
  263. resetAllBalls();
  264. }
  265. private:
  266. ThreadPool pool;
  267. TextButton controlButton;
  268. bool isUsingPool;
  269. OwnedArray<Component> balls;
  270. };
  271. //==============================================================================
  272. Component* createThreadingDemo()
  273. {
  274. return new ThreadingDemo();
  275. }