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.

343 lines
10.0KB

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