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
9.8KB

  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. auto angle = Random::getSystemRandom().nextFloat() * MathConstants<float>::twoPi;
  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. {
  161. public:
  162. MultithreadingDemo()
  163. {
  164. setOpaque (true);
  165. addAndMakeVisible (controlButton);
  166. controlButton.changeWidthToFitText (24);
  167. controlButton.setTopLeftPosition (20, 20);
  168. controlButton.setTriggeredOnMouseDown (true);
  169. controlButton.setAlwaysOnTop (true);
  170. controlButton.onClick = [this] { showMenu(); };
  171. }
  172. ~MultithreadingDemo()
  173. {
  174. pool.removeAllJobs (true, 2000);
  175. }
  176. void resetAllBalls()
  177. {
  178. stopTimer();
  179. pool.removeAllJobs (true, 4000);
  180. balls.clear();
  181. if (isShowing())
  182. {
  183. while (balls.size() < 5)
  184. addABall();
  185. startTimer (300);
  186. }
  187. }
  188. void paint (Graphics& g) override
  189. {
  190. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  191. }
  192. private:
  193. ThreadPool pool { 3 };
  194. TextButton controlButton { "Thread type" };
  195. bool isUsingPool = false;
  196. OwnedArray<Component> balls;
  197. void setUsingPool (bool usePool)
  198. {
  199. isUsingPool = usePool;
  200. resetAllBalls();
  201. }
  202. void addABall()
  203. {
  204. if (isUsingPool)
  205. {
  206. DemoThreadPoolJob* newBall = new DemoThreadPoolJob();
  207. balls.add (newBall);
  208. addAndMakeVisible (newBall);
  209. newBall->parentSizeChanged();
  210. pool.addJob (newBall, false);
  211. }
  212. else
  213. {
  214. DemoThread* newBall = new DemoThread();
  215. balls.add (newBall);
  216. addAndMakeVisible (newBall);
  217. newBall->parentSizeChanged();
  218. }
  219. }
  220. void removeABall()
  221. {
  222. if (balls.size() > 0)
  223. {
  224. int indexToRemove = Random::getSystemRandom().nextInt (balls.size());
  225. if (isUsingPool)
  226. pool.removeJob (dynamic_cast<DemoThreadPoolJob*> (balls [indexToRemove]), true, 4000);
  227. balls.remove (indexToRemove);
  228. }
  229. }
  230. // this gets called when a component is added or removed from a parent component.
  231. void parentHierarchyChanged() override
  232. {
  233. // we'll use this as an opportunity to start and stop the threads, so that
  234. // we don't leave them going when the component's not actually visible.
  235. resetAllBalls();
  236. }
  237. void timerCallback() override
  238. {
  239. if (Random::getSystemRandom().nextBool())
  240. {
  241. if (balls.size() <= 10)
  242. addABall();
  243. }
  244. else
  245. {
  246. if (balls.size() > 3)
  247. removeABall();
  248. }
  249. }
  250. void showMenu()
  251. {
  252. PopupMenu m;
  253. m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
  254. m.addItem (2, "Use a thread pool", true, isUsingPool);
  255. m.showMenuAsync (PopupMenu::Options().withTargetComponent (&controlButton),
  256. ModalCallbackFunction::forComponent (menuItemChosenCallback, this));
  257. }
  258. static void menuItemChosenCallback (int result, MultithreadingDemo* demoComponent)
  259. {
  260. if (result != 0 && demoComponent != nullptr)
  261. demoComponent->setUsingPool (result == 2);
  262. }
  263. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultithreadingDemo)
  264. };
  265. // This static object will register this demo type in a global list of demos..
  266. static JuceDemoType<MultithreadingDemo> demo ("40 Multi-threading");