git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
329
deps/juce/examples/Utilities/MultithreadingDemo.h
vendored
Normal file
329
deps/juce/examples/Utilities/MultithreadingDemo.h
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
|
||||
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
|
||||
PURPOSE, ARE DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this PIP. A PIP is a short snippet
|
||||
of code that can be read by the Projucer and used to generate a JUCE project.
|
||||
|
||||
BEGIN_JUCE_PIP_METADATA
|
||||
|
||||
name: MultithreadingDemo
|
||||
version: 1.0.0
|
||||
vendor: JUCE
|
||||
website: http://juce.com
|
||||
description: Demonstrates multi-threading.
|
||||
|
||||
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
|
||||
juce_gui_basics
|
||||
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
|
||||
|
||||
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
|
||||
|
||||
type: Component
|
||||
mainClass: MultithreadingDemo
|
||||
|
||||
useLocalCopy: 1
|
||||
|
||||
END_JUCE_PIP_METADATA
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Assets/DemoUtilities.h"
|
||||
|
||||
//==============================================================================
|
||||
class BouncingBall : private ComponentListener
|
||||
{
|
||||
public:
|
||||
BouncingBall (Component& comp)
|
||||
: containerComponent (comp)
|
||||
{
|
||||
containerComponent.addComponentListener (this);
|
||||
|
||||
auto speed = 5.0f; // give each ball a fixed speed so we can
|
||||
// see the effects of thread priority on how fast
|
||||
// they actually go.
|
||||
|
||||
auto angle = Random::getSystemRandom().nextFloat() * MathConstants<float>::twoPi;
|
||||
|
||||
dx = std::sin (angle) * speed;
|
||||
dy = std::cos (angle) * speed;
|
||||
|
||||
colour = Colour ((juce::uint32) Random::getSystemRandom().nextInt())
|
||||
.withAlpha (0.5f)
|
||||
.withBrightness (0.7f);
|
||||
|
||||
componentMovedOrResized (containerComponent, true, true);
|
||||
|
||||
x = Random::getSystemRandom().nextFloat() * parentWidth;
|
||||
y = Random::getSystemRandom().nextFloat() * parentHeight;
|
||||
}
|
||||
|
||||
~BouncingBall() override
|
||||
{
|
||||
containerComponent.removeComponentListener (this);
|
||||
}
|
||||
|
||||
// This will be called from the message thread
|
||||
void draw (Graphics& g)
|
||||
{
|
||||
const ScopedLock lock (drawing);
|
||||
|
||||
g.setColour (colour);
|
||||
g.fillEllipse (x, y, size, size);
|
||||
|
||||
g.setColour (Colours::black);
|
||||
g.setFont (10.0f);
|
||||
g.drawText (String::toHexString ((int64) threadId), Rectangle<float> (x, y, size, size), Justification::centred, false);
|
||||
}
|
||||
|
||||
void moveBall()
|
||||
{
|
||||
const ScopedLock lock (drawing);
|
||||
|
||||
threadId = Thread::getCurrentThreadId(); // this is so the component can print the thread ID inside the ball
|
||||
|
||||
x += dx;
|
||||
y += dy;
|
||||
|
||||
if (x < 0)
|
||||
dx = std::abs (dx);
|
||||
|
||||
if (x > parentWidth)
|
||||
dx = -std::abs (dx);
|
||||
|
||||
if (y < 0)
|
||||
dy = std::abs (dy);
|
||||
|
||||
if (y > parentHeight)
|
||||
dy = -std::abs (dy);
|
||||
}
|
||||
|
||||
private:
|
||||
void componentMovedOrResized (Component& comp, bool, bool) override
|
||||
{
|
||||
const ScopedLock lock (drawing);
|
||||
|
||||
parentWidth = (float) comp.getWidth() - size;
|
||||
parentHeight = (float) comp.getHeight() - size;
|
||||
}
|
||||
|
||||
float x = 0.0f, y = 0.0f,
|
||||
size = Random::getSystemRandom().nextFloat() * 30.0f + 30.0f,
|
||||
dx = 0.0f, dy = 0.0f,
|
||||
parentWidth = 50.0f, parentHeight = 50.0f;
|
||||
|
||||
Colour colour;
|
||||
Thread::ThreadID threadId = {};
|
||||
CriticalSection drawing;
|
||||
|
||||
Component& containerComponent;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBall)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class DemoThread : public BouncingBall,
|
||||
public Thread
|
||||
{
|
||||
public:
|
||||
DemoThread (Component& containerComp)
|
||||
: BouncingBall (containerComp),
|
||||
Thread ("JUCE Demo Thread")
|
||||
{
|
||||
// give the threads a random priority, so some will move more
|
||||
// smoothly than others..
|
||||
startThread (Random::getSystemRandom().nextInt (3) + 3);
|
||||
}
|
||||
|
||||
~DemoThread() override
|
||||
{
|
||||
// allow the thread 2 seconds to stop cleanly - should be plenty of time.
|
||||
stopThread (2000);
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
// this is the code that runs this thread - we'll loop continuously,
|
||||
// updating the coordinates of our blob.
|
||||
|
||||
// threadShouldExit() returns true when the stopThread() method has been
|
||||
// called, so we should check it often, and exit as soon as it gets flagged.
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
// sleep a bit so the threads don't all grind the CPU to a halt..
|
||||
wait (interval);
|
||||
|
||||
// because this is a background thread, we mustn't do any UI work without
|
||||
// first grabbing a MessageManagerLock..
|
||||
const MessageManagerLock mml (Thread::getCurrentThread());
|
||||
|
||||
if (! mml.lockWasGained()) // if something is trying to kill this job, the lock
|
||||
return; // will fail, in which case we'd better return..
|
||||
|
||||
// now we've got the UI thread locked, we can mess about with the components
|
||||
moveBall();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int interval = Random::getSystemRandom().nextInt (50) + 6;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThread)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class DemoThreadPoolJob : public BouncingBall,
|
||||
public ThreadPoolJob
|
||||
{
|
||||
public:
|
||||
DemoThreadPoolJob (Component& containerComp)
|
||||
: BouncingBall (containerComp),
|
||||
ThreadPoolJob ("Demo Threadpool Job")
|
||||
{}
|
||||
|
||||
JobStatus runJob() override
|
||||
{
|
||||
// this is the code that runs this job. It'll be repeatedly called until we return
|
||||
// jobHasFinished instead of jobNeedsRunningAgain.
|
||||
Thread::sleep (30);
|
||||
|
||||
// because this is a background thread, we mustn't do any UI work without
|
||||
// first grabbing a MessageManagerLock..
|
||||
const MessageManagerLock mml (this);
|
||||
|
||||
// before moving the ball, we need to check whether the lock was actually gained, because
|
||||
// if something is trying to stop this job, it will have failed..
|
||||
if (mml.lockWasGained())
|
||||
moveBall();
|
||||
|
||||
return jobNeedsRunningAgain;
|
||||
}
|
||||
|
||||
void removedFromQueue()
|
||||
{
|
||||
// This is called to tell us that our job has been removed from the pool.
|
||||
// In this case there's no need to do anything here.
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThreadPoolJob)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MultithreadingDemo : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MultithreadingDemo()
|
||||
{
|
||||
setOpaque (true);
|
||||
|
||||
addAndMakeVisible (controlButton);
|
||||
controlButton.changeWidthToFitText (24);
|
||||
controlButton.setTopLeftPosition (20, 20);
|
||||
controlButton.setTriggeredOnMouseDown (true);
|
||||
controlButton.setAlwaysOnTop (true);
|
||||
controlButton.onClick = [this] { showMenu(); };
|
||||
|
||||
setSize (500, 500);
|
||||
|
||||
resetAllBalls();
|
||||
|
||||
startTimerHz (60);
|
||||
}
|
||||
|
||||
~MultithreadingDemo() override
|
||||
{
|
||||
pool.removeAllJobs (true, 2000);
|
||||
}
|
||||
|
||||
void resetAllBalls()
|
||||
{
|
||||
pool.removeAllJobs (true, 4000);
|
||||
balls.clear();
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
addABall();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
|
||||
|
||||
for (auto* ball : balls)
|
||||
ball->draw (g);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void setUsingPool (bool usePool)
|
||||
{
|
||||
isUsingPool = usePool;
|
||||
resetAllBalls();
|
||||
}
|
||||
|
||||
void addABall()
|
||||
{
|
||||
if (isUsingPool)
|
||||
{
|
||||
auto newBall = std::make_unique<DemoThreadPoolJob> (*this);
|
||||
pool.addJob (newBall.get(), false);
|
||||
balls.add (newBall.release());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
balls.add (new DemoThread (*this));
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
void showMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
|
||||
m.addItem (2, "Use a thread pool", true, isUsingPool);
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetComponent (controlButton),
|
||||
ModalCallbackFunction::forComponent (menuItemChosenCallback, this));
|
||||
}
|
||||
|
||||
static void menuItemChosenCallback (int result, MultithreadingDemo* demoComponent)
|
||||
{
|
||||
if (result != 0 && demoComponent != nullptr)
|
||||
demoComponent->setUsingPool (result == 2);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ThreadPool pool { 3 };
|
||||
TextButton controlButton { "Thread type" };
|
||||
bool isUsingPool = false;
|
||||
|
||||
OwnedArray<BouncingBall> balls;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultithreadingDemo)
|
||||
};
|
Reference in New Issue
Block a user