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.

342 lines
10KB

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