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.

341 lines
9.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI 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 "../JuceDemoHeader.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 = std::sin (angle) * speed;
  36. dy = std::cos (angle) * speed;
  37. size = Random::getSystemRandom().nextFloat() * 30.0f + 30.0f;
  38. colour = Colour ((juce::uint32) Random::getSystemRandom().nextInt())
  39. .withAlpha (0.5f)
  40. .withBrightness (0.7f);
  41. }
  42. void paint (Graphics& g) override
  43. {
  44. g.setColour (colour);
  45. g.fillEllipse (innerX, innerY, size, size);
  46. g.setColour (Colours::black);
  47. g.setFont (10.0f);
  48. g.drawText (String::toHexString ((int64) threadId), getLocalBounds(), Justification::centred, false);
  49. }
  50. void parentSizeChanged() override
  51. {
  52. parentWidth = getParentWidth() - size;
  53. parentHeight = getParentHeight() - size;
  54. }
  55. void moveBall()
  56. {
  57. threadId = Thread::getCurrentThreadId(); // this is so the component can print the thread ID inside the ball
  58. x += dx;
  59. y += dy;
  60. if (x < 0)
  61. dx = std::abs (dx);
  62. if (x > parentWidth)
  63. dx = -std::abs (dx);
  64. if (y < 0)
  65. dy = std::abs (dy);
  66. if (y > parentHeight)
  67. dy = -std::abs (dy);
  68. setBounds (((int) x) - 2,
  69. ((int) y) - 2,
  70. ((int) size) + 4,
  71. ((int) size) + 4);
  72. innerX = x - getX();
  73. innerY = y - getY();
  74. repaint();
  75. }
  76. private:
  77. float x, y, size, dx, dy, parentWidth, parentHeight;
  78. float innerX, innerY;
  79. Colour colour;
  80. Thread::ThreadID threadId;
  81. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallComp)
  82. };
  83. //==============================================================================
  84. class DemoThread : public BouncingBallComp,
  85. public Thread
  86. {
  87. public:
  88. DemoThread()
  89. : Thread ("Juce Demo Thread")
  90. {
  91. interval = Random::getSystemRandom().nextInt (50) + 6;
  92. // give the threads a random priority, so some will move more
  93. // smoothly than others..
  94. startThread (Random::getSystemRandom().nextInt (3) + 3);
  95. }
  96. ~DemoThread()
  97. {
  98. // allow the thread 2 seconds to stop cleanly - should be plenty of time.
  99. stopThread (2000);
  100. }
  101. void run() override
  102. {
  103. // this is the code that runs this thread - we'll loop continuously,
  104. // updating the coordinates of our blob.
  105. // threadShouldExit() returns true when the stopThread() method has been
  106. // called, so we should check it often, and exit as soon as it gets flagged.
  107. while (! threadShouldExit())
  108. {
  109. // sleep a bit so the threads don't all grind the CPU to a halt..
  110. wait (interval);
  111. // because this is a background thread, we mustn't do any UI work without
  112. // first grabbing a MessageManagerLock..
  113. const MessageManagerLock mml (Thread::getCurrentThread());
  114. if (! mml.lockWasGained()) // if something is trying to kill this job, the lock
  115. return; // will fail, in which case we'd better return..
  116. // now we've got the UI thread locked, we can mess about with the components
  117. moveBall();
  118. }
  119. }
  120. private:
  121. int interval;
  122. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThread)
  123. };
  124. //==============================================================================
  125. class DemoThreadPoolJob : public BouncingBallComp,
  126. public ThreadPoolJob
  127. {
  128. public:
  129. DemoThreadPoolJob()
  130. : ThreadPoolJob ("Demo Threadpool Job")
  131. {
  132. }
  133. JobStatus runJob() override
  134. {
  135. // this is the code that runs this job. It'll be repeatedly called until we return
  136. // jobHasFinished instead of jobNeedsRunningAgain.
  137. Thread::sleep (30);
  138. // because this is a background thread, we mustn't do any UI work without
  139. // first grabbing a MessageManagerLock..
  140. const MessageManagerLock mml (this);
  141. // before moving the ball, we need to check whether the lock was actually gained, because
  142. // if something is trying to stop this job, it will have failed..
  143. if (mml.lockWasGained())
  144. moveBall();
  145. return jobNeedsRunningAgain;
  146. }
  147. void removedFromQueue()
  148. {
  149. // This is called to tell us that our job has been removed from the pool.
  150. // In this case there's no need to do anything here.
  151. }
  152. private:
  153. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThreadPoolJob)
  154. };
  155. //==============================================================================
  156. class MultithreadingDemo : public Component,
  157. private Timer,
  158. private Button::Listener
  159. {
  160. public:
  161. MultithreadingDemo()
  162. : pool (3),
  163. controlButton ("Thread type"),
  164. isUsingPool (false)
  165. {
  166. setOpaque (true);
  167. addAndMakeVisible (controlButton);
  168. controlButton.changeWidthToFitText (24);
  169. controlButton.setTopLeftPosition (20, 20);
  170. controlButton.setTriggeredOnMouseDown (true);
  171. controlButton.setAlwaysOnTop (true);
  172. controlButton.addListener (this);
  173. }
  174. ~MultithreadingDemo()
  175. {
  176. pool.removeAllJobs (true, 2000);
  177. }
  178. void resetAllBalls()
  179. {
  180. stopTimer();
  181. pool.removeAllJobs (true, 4000);
  182. balls.clear();
  183. if (isShowing())
  184. {
  185. while (balls.size() < 5)
  186. addABall();
  187. startTimer (300);
  188. }
  189. }
  190. void paint (Graphics& g) override
  191. {
  192. fillStandardDemoBackground (g);
  193. }
  194. private:
  195. ThreadPool pool;
  196. TextButton controlButton;
  197. bool isUsingPool;
  198. OwnedArray<Component> balls;
  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. // this gets called when a component is added or removed from a parent component.
  233. void parentHierarchyChanged() override
  234. {
  235. // we'll use this as an opportunity to start and stop the threads, so that
  236. // we don't leave them going when the component's not actually visible.
  237. resetAllBalls();
  238. }
  239. void timerCallback() override
  240. {
  241. if (Random::getSystemRandom().nextBool())
  242. {
  243. if (balls.size() <= 10)
  244. addABall();
  245. }
  246. else
  247. {
  248. if (balls.size() > 3)
  249. removeABall();
  250. }
  251. }
  252. void buttonClicked (Button*) override
  253. {
  254. PopupMenu m;
  255. m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
  256. m.addItem (2, "Use a thread pool", true, isUsingPool);
  257. m.showMenuAsync (PopupMenu::Options().withTargetComponent (&controlButton),
  258. ModalCallbackFunction::forComponent (menuItemChosenCallback, this));
  259. }
  260. static void menuItemChosenCallback (int result, MultithreadingDemo* demoComponent)
  261. {
  262. if (result != 0 && demoComponent != nullptr)
  263. demoComponent->setUsingPool (result == 2);
  264. }
  265. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultithreadingDemo)
  266. };
  267. // This static object will register this demo type in a global list of demos..
  268. static JuceDemoType<MultithreadingDemo> demo ("40 Multi-threading");