mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Thread: Introduce a new Thread backend
This is a breaking change - see BREAKING-CHANGES.txt
This commit is contained in:
parent
621e14d092
commit
d3cff375be
39 changed files with 849 additions and 603 deletions
|
|
@ -1,9 +1,37 @@
|
||||||
JUCE breaking changes
|
JUCE breaking changes
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
develop
|
Develop
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
Change
|
||||||
|
------
|
||||||
|
The Thread::startThread (int) and Thread::setPriority (int) methods have been
|
||||||
|
removed. A new Thread priority API has been introduced.
|
||||||
|
|
||||||
|
Possible Issues
|
||||||
|
---------------
|
||||||
|
Code will fail to compile.
|
||||||
|
|
||||||
|
Workaround
|
||||||
|
----------
|
||||||
|
Rather than using an integer thread priority you must instead use a value from
|
||||||
|
the Thread::Priority enum. Thread::setPriority and Thread::getPriority should
|
||||||
|
only be called from the target thread. To start a Thread with a realtime
|
||||||
|
performance profile you must call startRealtimeThread.
|
||||||
|
|
||||||
|
Rationale
|
||||||
|
---------
|
||||||
|
Operating systems are moving away from a specific thread priority and towards
|
||||||
|
more granular control over which types of cores can be used and things like
|
||||||
|
power throttling options. In particular, it is no longer possible to map a 0-10
|
||||||
|
integer to a meaningful performance range on macOS ARM using the pthread
|
||||||
|
interface. Using a more modern interface grants us access to more runtime
|
||||||
|
options, but also changes how we can work with threads. The two most
|
||||||
|
significant changes are that we cannot mix operations using the new and old
|
||||||
|
interfaces, and that changing a priority using the new interface can only be
|
||||||
|
done on the currently running thread.
|
||||||
|
|
||||||
Change
|
Change
|
||||||
------
|
------
|
||||||
The constructor of WebBrowserComponent now requires passing in an instance of
|
The constructor of WebBrowserComponent now requires passing in an instance of
|
||||||
|
|
@ -40,7 +68,6 @@ browser backend is more intuitive then requiring the user to derive from a
|
||||||
special class, especially if additional browser backends are added in the
|
special class, especially if additional browser backends are added in the
|
||||||
future.
|
future.
|
||||||
|
|
||||||
|
|
||||||
Change
|
Change
|
||||||
------
|
------
|
||||||
The function AudioIODeviceCallback::audioDeviceIOCallback() was removed.
|
The function AudioIODeviceCallback::audioDeviceIOCallback() was removed.
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,7 @@ public:
|
||||||
// audio setup
|
// audio setup
|
||||||
formatManager.registerBasicFormats();
|
formatManager.registerBasicFormats();
|
||||||
|
|
||||||
thread.startThread (3);
|
thread.startThread (Thread::Priority::normal);
|
||||||
|
|
||||||
#ifndef JUCE_DEMO_RUNNER
|
#ifndef JUCE_DEMO_RUNNER
|
||||||
RuntimePermissions::request (RuntimePermissions::recordAudio,
|
RuntimePermissions::request (RuntimePermissions::recordAudio,
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ public:
|
||||||
{
|
{
|
||||||
setOpaque (true);
|
setOpaque (true);
|
||||||
imageList.setDirectory (File::getSpecialLocation (File::userPicturesDirectory), true, true);
|
imageList.setDirectory (File::getSpecialLocation (File::userPicturesDirectory), true, true);
|
||||||
directoryThread.startThread (1);
|
directoryThread.startThread (Thread::Priority::background);
|
||||||
|
|
||||||
fileTree.setTitle ("Files");
|
fileTree.setTitle ("Files");
|
||||||
fileTree.addListener (this);
|
fileTree.addListener (this);
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ public:
|
||||||
setOpaque (true);
|
setOpaque (true);
|
||||||
|
|
||||||
movieList.setDirectory (File::getSpecialLocation (File::userMoviesDirectory), true, true);
|
movieList.setDirectory (File::getSpecialLocation (File::userMoviesDirectory), true, true);
|
||||||
directoryThread.startThread (1);
|
directoryThread.startThread (Thread::Priority::background);
|
||||||
|
|
||||||
fileTree.setTitle ("Files");
|
fileTree.setTitle ("Files");
|
||||||
fileTree.addListener (this);
|
fileTree.addListener (this);
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ public:
|
||||||
SharedTimeSliceThread()
|
SharedTimeSliceThread()
|
||||||
: TimeSliceThread (String (JucePlugin_Name) + " ARA Sample Reading Thread")
|
: TimeSliceThread (String (JucePlugin_Name) + " ARA Sample Reading Thread")
|
||||||
{
|
{
|
||||||
startThread (7); // Above default priority so playback is fluent, but below realtime
|
startThread (Priority::high); // Above default priority so playback is fluent, but below realtime
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,9 +148,7 @@ public:
|
||||||
: BouncingBall (containerComp),
|
: BouncingBall (containerComp),
|
||||||
Thread ("JUCE Demo Thread")
|
Thread ("JUCE Demo Thread")
|
||||||
{
|
{
|
||||||
// give the threads a random priority, so some will move more
|
startThread();
|
||||||
// smoothly than others..
|
|
||||||
startThread (Random::getSystemRandom().nextInt (3) + 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~DemoThread() override
|
~DemoThread() override
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ void LatestVersionCheckerAndUpdater::checkForNewVersion (bool background)
|
||||||
if (! isThreadRunning())
|
if (! isThreadRunning())
|
||||||
{
|
{
|
||||||
backgroundCheck = background;
|
backgroundCheck = background;
|
||||||
startThread (3);
|
startThread (Priority::low);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,7 +373,7 @@ public:
|
||||||
: ThreadWithProgressWindow ("Downloading New Version", true, true),
|
: ThreadWithProgressWindow ("Downloading New Version", true, true),
|
||||||
asset (a), targetFolder (t), completionCallback (std::move (cb))
|
asset (a), targetFolder (t), completionCallback (std::move (cb))
|
||||||
{
|
{
|
||||||
launchThread (3);
|
launchThread (Priority::low);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ void MidiOutput::clearAllPendingMessages()
|
||||||
|
|
||||||
void MidiOutput::startBackgroundThread()
|
void MidiOutput::startBackgroundThread()
|
||||||
{
|
{
|
||||||
startThread (9);
|
startThread (Priority::high);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiOutput::stopBackgroundThread()
|
void MidiOutput::stopBackgroundThread()
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,7 @@ public:
|
||||||
if (inputDevice != nullptr)
|
if (inputDevice != nullptr)
|
||||||
env->CallVoidMethod (inputDevice, AudioRecord.startRecording);
|
env->CallVoidMethod (inputDevice, AudioRecord.startRecording);
|
||||||
|
|
||||||
startThread (8);
|
startThread (Priority::high);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -618,7 +618,7 @@ public:
|
||||||
if (outputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (outputDevice->handle)))
|
if (outputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (outputDevice->handle)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
startThread (9);
|
startThread (Priority::high);
|
||||||
|
|
||||||
int count = 1000;
|
int count = 1000;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1196,7 +1196,7 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels,
|
||||||
for (int i = 0; i < inChans.size(); ++i)
|
for (int i = 0; i < inChans.size(); ++i)
|
||||||
inChans.getUnchecked(i)->synchronisePosition();
|
inChans.getUnchecked(i)->synchronisePosition();
|
||||||
|
|
||||||
startThread (9);
|
startThread (Priority::highest);
|
||||||
sleep (10);
|
sleep (10);
|
||||||
|
|
||||||
notify();
|
notify();
|
||||||
|
|
|
||||||
|
|
@ -1387,7 +1387,7 @@ public:
|
||||||
shouldShutdown = false;
|
shouldShutdown = false;
|
||||||
deviceSampleRateChanged = false;
|
deviceSampleRateChanged = false;
|
||||||
|
|
||||||
startThread (8);
|
startThread (Priority::high);
|
||||||
Thread::sleep (5);
|
Thread::sleep (5);
|
||||||
|
|
||||||
if (inputDevice != nullptr && inputDevice->client != nullptr)
|
if (inputDevice != nullptr && inputDevice->client != nullptr)
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ public:
|
||||||
void runTest() override
|
void runTest() override
|
||||||
{
|
{
|
||||||
TimeSliceThread timeSlice ("TestBackgroundThread");
|
TimeSliceThread timeSlice ("TestBackgroundThread");
|
||||||
timeSlice.startThread (5);
|
timeSlice.startThread (Thread::Priority::normal);
|
||||||
|
|
||||||
beginTest ("Timeout");
|
beginTest ("Timeout");
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@
|
||||||
|
|
||||||
#if JUCE_LINUX || JUCE_BSD
|
#if JUCE_LINUX || JUCE_BSD
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace juce
|
namespace juce
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -34,15 +32,15 @@ namespace juce
|
||||||
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages);
|
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages);
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
class MessageThread
|
class MessageThread : public Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MessageThread()
|
MessageThread() : Thread ("JUCE Plugin Message Thread")
|
||||||
{
|
{
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
~MessageThread()
|
~MessageThread() override
|
||||||
{
|
{
|
||||||
MessageManager::getInstance()->stopDispatchLoop();
|
MessageManager::getInstance()->stopDispatchLoop();
|
||||||
stop();
|
stop();
|
||||||
|
|
@ -50,51 +48,37 @@ public:
|
||||||
|
|
||||||
void start()
|
void start()
|
||||||
{
|
{
|
||||||
if (isRunning())
|
startThread (Priority::high);
|
||||||
stop();
|
|
||||||
|
|
||||||
shouldExit = false;
|
// Wait for setCurrentThreadAsMessageThread() and getInstance to be executed
|
||||||
|
// before leaving this method
|
||||||
thread = std::thread { [this]
|
threadInitialised.wait (10000);
|
||||||
{
|
|
||||||
Thread::setCurrentThreadPriority (7);
|
|
||||||
Thread::setCurrentThreadName ("JUCE Plugin Message Thread");
|
|
||||||
|
|
||||||
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
|
|
||||||
XWindowSystem::getInstance();
|
|
||||||
|
|
||||||
threadInitialised.signal();
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (! dispatchNextMessageOnSystemQueue (true))
|
|
||||||
Thread::sleep (1);
|
|
||||||
|
|
||||||
if (shouldExit)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} };
|
|
||||||
|
|
||||||
threadInitialised.wait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
if (! isRunning())
|
signalThreadShouldExit();
|
||||||
return;
|
stopThread (-1);
|
||||||
|
|
||||||
shouldExit = true;
|
|
||||||
thread.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isRunning() const noexcept { return thread.joinable(); }
|
bool isRunning() const noexcept { return isThreadRunning(); }
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
|
||||||
|
XWindowSystem::getInstance();
|
||||||
|
|
||||||
|
threadInitialised.signal();
|
||||||
|
|
||||||
|
while (! threadShouldExit())
|
||||||
|
{
|
||||||
|
if (! dispatchNextMessageOnSystemQueue (true))
|
||||||
|
Thread::sleep (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WaitableEvent threadInitialised;
|
WaitableEvent threadInitialised;
|
||||||
std::thread thread;
|
|
||||||
|
|
||||||
std::atomic<bool> shouldExit { false };
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_MOVEABLE (MessageThread)
|
JUCE_DECLARE_NON_MOVEABLE (MessageThread)
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageThread)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageThread)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ AudioThumbnailCache::AudioThumbnailCache (const int maxNumThumbs)
|
||||||
maxNumThumbsToStore (maxNumThumbs)
|
maxNumThumbsToStore (maxNumThumbs)
|
||||||
{
|
{
|
||||||
jassert (maxNumThumbsToStore > 0);
|
jassert (maxNumThumbsToStore > 0);
|
||||||
thread.startThread (2);
|
thread.startThread (Thread::Priority::low);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioThumbnailCache::~AudioThumbnailCache()
|
AudioThumbnailCache::~AudioThumbnailCache()
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,7 @@
|
||||||
#include "zip/juce_ZipFile.cpp"
|
#include "zip/juce_ZipFile.cpp"
|
||||||
#include "files/juce_FileFilter.cpp"
|
#include "files/juce_FileFilter.cpp"
|
||||||
#include "files/juce_WildcardFileFilter.cpp"
|
#include "files/juce_WildcardFileFilter.cpp"
|
||||||
|
#include "native/juce_native_ThreadPriorities.h"
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#if ! JUCE_WINDOWS
|
#if ! JUCE_WINDOWS
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,7 @@
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
|
||||||
|
|
@ -344,36 +344,79 @@ LocalRef<jobject> getMainActivity() noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime
|
#if JUCE_ANDROID && JUCE_MODULE_AVAILABLE_juce_audio_devices && (JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE)
|
||||||
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior)
|
#define JUCE_ANDROID_REALTIME_THREAD_AVAILABLE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JUCE_ANDROID_REALTIME_THREAD_AVAILABLE
|
||||||
|
pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern JavaVM* androidJNIJavaVM;
|
||||||
|
|
||||||
|
bool Thread::createNativeThread (Priority)
|
||||||
{
|
{
|
||||||
// TODO
|
if (isRealtime())
|
||||||
|
{
|
||||||
|
#if JUCE_ANDROID_REALTIME_THREAD_AVAILABLE
|
||||||
|
threadHandle = (void*) juce_createRealtimeAudioThread (threadEntryProc, this);
|
||||||
|
threadId = (ThreadID) threadHandle.get();
|
||||||
|
return threadId != nullptr;
|
||||||
|
#else
|
||||||
|
jassertfalse;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
struct sched_param param;
|
PosixThreadAttribute attr { threadStackSize };
|
||||||
int policy, maxp, minp;
|
threadId = threadHandle = makeThreadHandle (attr, this, [] (void* userData) -> void*
|
||||||
|
{
|
||||||
|
auto* myself = static_cast<Thread*> (userData);
|
||||||
|
|
||||||
const int p = (int) prior;
|
juce_threadEntryPoint (myself);
|
||||||
|
|
||||||
if (p <= 1)
|
if (androidJNIJavaVM != nullptr)
|
||||||
policy = SCHED_OTHER;
|
{
|
||||||
else
|
void* env = nullptr;
|
||||||
policy = SCHED_RR;
|
androidJNIJavaVM->GetEnv (&env, JNI_VERSION_1_2);
|
||||||
|
|
||||||
minp = sched_get_priority_min (policy);
|
// only detach if we have actually been attached
|
||||||
maxp = sched_get_priority_max (policy);
|
if (env != nullptr)
|
||||||
|
androidJNIJavaVM->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
|
||||||
if (p < 2)
|
return nullptr;
|
||||||
param.sched_priority = 0;
|
});
|
||||||
else if (p == 2 )
|
|
||||||
// Set to middle of lower realtime priority range
|
|
||||||
param.sched_priority = minp + (maxp - minp) / 4;
|
|
||||||
else
|
|
||||||
// Set to middle of higher realtime priority range
|
|
||||||
param.sched_priority = minp + (3 * (maxp - minp) / 4);
|
|
||||||
|
|
||||||
pthread_setschedparam (pthread_self(), policy, ¶m);
|
return threadId != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::killThread()
|
||||||
|
{
|
||||||
|
if (threadHandle != nullptr)
|
||||||
|
jassertfalse; // pthread_cancel not available!
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread::Priority Thread::getPriority() const
|
||||||
|
{
|
||||||
|
jassert (Thread::getCurrentThreadId() == getThreadId());
|
||||||
|
|
||||||
|
const auto native = getpriority (PRIO_PROCESS, (id_t) gettid());
|
||||||
|
return ThreadPriorities::getJucePriority (native);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::setPriority (Priority)
|
||||||
|
{
|
||||||
|
jassert (Thread::getCurrentThreadId() == getThreadId());
|
||||||
|
|
||||||
|
if (isRealtime())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return setpriority (PRIO_PROCESS, (id_t) gettid(), ThreadPriorities::getNativePriority (priority)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) {}
|
||||||
|
|
||||||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
||||||
{
|
{
|
||||||
StringArray lines;
|
StringArray lines;
|
||||||
|
|
|
||||||
|
|
@ -28,27 +28,49 @@ namespace juce
|
||||||
live in juce_posix_SharedCode.h!
|
live in juce_posix_SharedCode.h!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//==============================================================================
|
bool Thread::createNativeThread (Priority)
|
||||||
JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior)
|
|
||||||
{
|
{
|
||||||
auto policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR;
|
PosixThreadAttribute attr { threadStackSize };
|
||||||
auto minp = sched_get_priority_min (policy);
|
PosixSchedulerPriority::getNativeSchedulerAndPriority (realtimeOptions, {}).apply (attr);
|
||||||
auto maxp = sched_get_priority_max (policy);
|
|
||||||
|
|
||||||
struct sched_param param;
|
threadId = threadHandle = makeThreadHandle (attr, this, [] (void* userData) -> void*
|
||||||
|
|
||||||
switch (prior)
|
|
||||||
{
|
{
|
||||||
case LowPriority:
|
auto* myself = static_cast<Thread*> (userData);
|
||||||
case NormalPriority: param.sched_priority = 0; break;
|
|
||||||
case HighPriority: param.sched_priority = minp + (maxp - minp) / 4; break;
|
|
||||||
case RealtimePriority: param.sched_priority = minp + (3 * (maxp - minp) / 4); break;
|
|
||||||
default: jassertfalse; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_setschedparam (pthread_self(), policy, ¶m);
|
juce_threadEntryPoint (myself);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
return threadId != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::killThread()
|
||||||
|
{
|
||||||
|
if (threadHandle != nullptr)
|
||||||
|
pthread_cancel ((pthread_t) threadHandle.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Until we implement Nice awareness, these don't do anything on Linux.
|
||||||
|
Thread::Priority Thread::getPriority() const
|
||||||
|
{
|
||||||
|
jassert (Thread::getCurrentThreadId() == getThreadId());
|
||||||
|
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::setPriority (Priority newPriority)
|
||||||
|
{
|
||||||
|
jassert (Thread::getCurrentThreadId() == getThreadId());
|
||||||
|
|
||||||
|
// Return true to make it compatible with other platforms.
|
||||||
|
priority = newPriority;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) {}
|
||||||
|
|
||||||
static bool swapUserAndEffectiveUser()
|
static bool swapUserAndEffectiveUser()
|
||||||
{
|
{
|
||||||
auto result1 = setreuid (geteuid(), getuid());
|
auto result1 = setreuid (geteuid(), getuid());
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,139 @@ namespace juce
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if JUCE_IOS
|
#if JUCE_IOS
|
||||||
bool isIOSAppActive = true;
|
bool isIOSAppActive = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
API_AVAILABLE (macos (10.10))
|
||||||
|
static auto getNativeQOS (Thread::Priority priority)
|
||||||
|
{
|
||||||
|
switch (priority)
|
||||||
|
{
|
||||||
|
case Thread::Priority::highest: return QOS_CLASS_USER_INTERACTIVE;
|
||||||
|
case Thread::Priority::high: return QOS_CLASS_USER_INITIATED;
|
||||||
|
case Thread::Priority::low: return QOS_CLASS_UTILITY;
|
||||||
|
case Thread::Priority::background: return QOS_CLASS_BACKGROUND;
|
||||||
|
case Thread::Priority::normal: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QOS_CLASS_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
API_AVAILABLE (macos (10.10))
|
||||||
|
static auto getJucePriority (qos_class_t qos)
|
||||||
|
{
|
||||||
|
switch (qos)
|
||||||
|
{
|
||||||
|
case QOS_CLASS_USER_INTERACTIVE: return Thread::Priority::highest;
|
||||||
|
case QOS_CLASS_USER_INITIATED: return Thread::Priority::high;
|
||||||
|
case QOS_CLASS_UTILITY: return Thread::Priority::low;
|
||||||
|
case QOS_CLASS_BACKGROUND: return Thread::Priority::background;
|
||||||
|
|
||||||
|
case QOS_CLASS_UNSPECIFIED:
|
||||||
|
case QOS_CLASS_DEFAULT: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Thread::Priority::normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::createNativeThread (Priority priority)
|
||||||
|
{
|
||||||
|
PosixThreadAttribute attr { threadStackSize };
|
||||||
|
|
||||||
|
if (@available (macos 10.10, *))
|
||||||
|
pthread_attr_set_qos_class_np (attr.get(), getNativeQOS (priority), 0);
|
||||||
|
else
|
||||||
|
PosixSchedulerPriority::getNativeSchedulerAndPriority (realtimeOptions, priority).apply (attr);
|
||||||
|
|
||||||
|
threadId = threadHandle = makeThreadHandle (attr, this, [] (void* userData) -> void*
|
||||||
|
{
|
||||||
|
auto* myself = static_cast<Thread*> (userData);
|
||||||
|
|
||||||
|
JUCE_AUTORELEASEPOOL
|
||||||
|
{
|
||||||
|
juce_threadEntryPoint (myself);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
return threadId != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::killThread()
|
||||||
|
{
|
||||||
|
if (threadHandle != nullptr)
|
||||||
|
pthread_cancel ((pthread_t) threadHandle.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread::Priority Thread::getPriority() const
|
||||||
|
{
|
||||||
|
jassert (Thread::getCurrentThreadId() == getThreadId());
|
||||||
|
|
||||||
|
if (! isRealtime())
|
||||||
|
{
|
||||||
|
if (@available (macOS 10.10, *))
|
||||||
|
return getJucePriority (qos_class_self());
|
||||||
|
|
||||||
|
// fallback for older versions of macOS
|
||||||
|
const auto min = jmax (0, sched_get_priority_min (SCHED_OTHER));
|
||||||
|
const auto max = jmax (0, sched_get_priority_max (SCHED_OTHER));
|
||||||
|
|
||||||
|
if (min != 0 && max != 0)
|
||||||
|
{
|
||||||
|
const auto native = PosixSchedulerPriority::findCurrentSchedulerAndPriority().getPriority();
|
||||||
|
const auto mapped = jmap (native, min, max, 0, 4);
|
||||||
|
return ThreadPriorities::getJucePriority (mapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::setPriority (Priority priority)
|
||||||
|
{
|
||||||
|
jassert (Thread::getCurrentThreadId() == getThreadId());
|
||||||
|
|
||||||
|
if (isRealtime())
|
||||||
|
{
|
||||||
|
// macOS/iOS needs to know how much time you need!
|
||||||
|
jassert (realtimeOptions->workDurationMs > 0);
|
||||||
|
|
||||||
|
mach_timebase_info_data_t timebase;
|
||||||
|
mach_timebase_info (&timebase);
|
||||||
|
|
||||||
|
const auto periodMs = realtimeOptions->workDurationMs;
|
||||||
|
const auto ticksPerMs = ((double) timebase.denom * 1000000.0) / (double) timebase.numer;
|
||||||
|
const auto periodTicks = (uint32_t) jmin ((double) std::numeric_limits<uint32_t>::max(), periodMs * ticksPerMs);
|
||||||
|
|
||||||
|
thread_time_constraint_policy_data_t policy;
|
||||||
|
policy.period = periodTicks;
|
||||||
|
policy.computation = jmin ((uint32_t) 50000, policy.period);
|
||||||
|
policy.constraint = policy.period;
|
||||||
|
policy.preemptible = true;
|
||||||
|
|
||||||
|
return thread_policy_set (pthread_mach_thread_np (pthread_self()),
|
||||||
|
THREAD_TIME_CONSTRAINT_POLICY,
|
||||||
|
(thread_policy_t) &policy,
|
||||||
|
THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@available (macOS 10.10, *))
|
||||||
|
return pthread_set_qos_class_self_np (getNativeQOS (priority), 0) == 0;
|
||||||
|
|
||||||
|
#if JUCE_ARM
|
||||||
|
// M1 platforms should never reach this code!!!!!!
|
||||||
|
jassertfalse;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Just in case older versions of macOS support SCHED_OTHER priorities.
|
||||||
|
const auto psp = PosixSchedulerPriority::getNativeSchedulerAndPriority ({}, priority);
|
||||||
|
|
||||||
|
struct sched_param param;
|
||||||
|
param.sched_priority = psp.getPriority();
|
||||||
|
return pthread_setschedparam (pthread_self(), psp.getScheduler(), ¶m) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess()
|
JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
109
modules/juce_core/native/juce_native_ThreadPriorities.h
Normal file
109
modules/juce_core/native/juce_native_ThreadPriorities.h
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
This file is part of the JUCE library.
|
||||||
|
Copyright (c) 2022 - Raw Material Software Limited
|
||||||
|
|
||||||
|
JUCE is an open source library subject to commercial or open-source
|
||||||
|
licensing.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||||
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||||
|
DISCLAIMED.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace juce
|
||||||
|
{
|
||||||
|
|
||||||
|
struct ThreadPriorities
|
||||||
|
{
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
Thread::Priority priority;
|
||||||
|
int native;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if JUCE_ANDROID
|
||||||
|
enum AndroidThreadPriority
|
||||||
|
{
|
||||||
|
THREAD_PRIORITY_AUDIO = -16,
|
||||||
|
THREAD_PRIORITY_FOREGROUND = -2,
|
||||||
|
THREAD_PRIORITY_MORE_FAVORABLE = -1,
|
||||||
|
THREAD_PRIORITY_DEFAULT = 0,
|
||||||
|
THREAD_PRIORITY_LESS_FAVORABLE = 1,
|
||||||
|
THREAD_PRIORITY_BACKGROUND = 10,
|
||||||
|
THREAD_PRIORITY_LOWEST = 19
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline static constexpr Entry table[]
|
||||||
|
{
|
||||||
|
#if JUCE_ANDROID
|
||||||
|
{ Thread::Priority::highest, AndroidThreadPriority::THREAD_PRIORITY_AUDIO },
|
||||||
|
{ Thread::Priority::high, AndroidThreadPriority::THREAD_PRIORITY_FOREGROUND },
|
||||||
|
{ Thread::Priority::normal, AndroidThreadPriority::THREAD_PRIORITY_DEFAULT },
|
||||||
|
{ Thread::Priority::low, AndroidThreadPriority::THREAD_PRIORITY_BACKGROUND - 5 },
|
||||||
|
{ Thread::Priority::background, AndroidThreadPriority::THREAD_PRIORITY_BACKGROUND },
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JUCE_LINUX || JUCE_BSD
|
||||||
|
{ Thread::Priority::highest, 0 },
|
||||||
|
{ Thread::Priority::high, 0 },
|
||||||
|
{ Thread::Priority::normal, 0 },
|
||||||
|
{ Thread::Priority::low, 0 },
|
||||||
|
{ Thread::Priority::background, 0 },
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JUCE_MAC | JUCE_IOS
|
||||||
|
{ Thread::Priority::highest, 4 },
|
||||||
|
{ Thread::Priority::high, 3 },
|
||||||
|
{ Thread::Priority::normal, 2 },
|
||||||
|
{ Thread::Priority::low, 1 },
|
||||||
|
{ Thread::Priority::background, 0 },
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
{ Thread::Priority::highest, THREAD_PRIORITY_TIME_CRITICAL },
|
||||||
|
{ Thread::Priority::high, THREAD_PRIORITY_HIGHEST },
|
||||||
|
{ Thread::Priority::normal, THREAD_PRIORITY_NORMAL },
|
||||||
|
{ Thread::Priority::low, THREAD_PRIORITY_LOWEST },
|
||||||
|
{ Thread::Priority::background, THREAD_PRIORITY_IDLE },
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert (std::size (table) == 5,
|
||||||
|
"The platform may be unsupported or there may be a priority entry missing.");
|
||||||
|
|
||||||
|
static Thread::Priority getJucePriority (const int value)
|
||||||
|
{
|
||||||
|
const auto iter = std::min_element (std::begin (table),
|
||||||
|
std::end (table),
|
||||||
|
[value] (const auto& a, const auto& b)
|
||||||
|
{
|
||||||
|
return std::abs (a.native - value) < std::abs (b.native - value);
|
||||||
|
});
|
||||||
|
|
||||||
|
jassert (iter != std::end (table));
|
||||||
|
return iter != std::end (table) ? iter->priority : Thread::Priority{};
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getNativePriority (const Thread::Priority value)
|
||||||
|
{
|
||||||
|
const auto iter = std::find_if (std::begin (table),
|
||||||
|
std::end (table),
|
||||||
|
[value] (const auto& entry) { return entry.priority == value; });
|
||||||
|
|
||||||
|
jassert (iter != std::end (table));
|
||||||
|
return iter != std::end (table) ? iter->native : 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace juce
|
||||||
|
|
@ -851,98 +851,131 @@ void InterProcessLock::exit()
|
||||||
pimpl.reset();
|
pimpl.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
class PosixThreadAttribute
|
||||||
#if JUCE_ANDROID
|
|
||||||
extern JavaVM* androidJNIJavaVM;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void* threadEntryProc (void* userData)
|
|
||||||
{
|
{
|
||||||
auto* myself = static_cast<Thread*> (userData);
|
public:
|
||||||
|
explicit PosixThreadAttribute (size_t stackSize)
|
||||||
JUCE_AUTORELEASEPOOL
|
|
||||||
{
|
{
|
||||||
juce_threadEntryPoint (myself);
|
if (valid)
|
||||||
|
pthread_attr_setstacksize (&attr, stackSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JUCE_ANDROID
|
~PosixThreadAttribute()
|
||||||
if (androidJNIJavaVM != nullptr)
|
|
||||||
{
|
{
|
||||||
void* env = nullptr;
|
if (valid)
|
||||||
androidJNIJavaVM->GetEnv(&env, JNI_VERSION_1_2);
|
pthread_attr_destroy (&attr);
|
||||||
|
|
||||||
// only detach if we have actually been attached
|
|
||||||
if (env != nullptr)
|
|
||||||
androidJNIJavaVM->DetachCurrentThread();
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return nullptr;
|
auto* get() { return valid ? &attr : nullptr; }
|
||||||
}
|
|
||||||
|
|
||||||
#if JUCE_ANDROID && JUCE_MODULE_AVAILABLE_juce_audio_devices && (JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE)
|
private:
|
||||||
#define JUCE_ANDROID_REALTIME_THREAD_AVAILABLE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JUCE_ANDROID_REALTIME_THREAD_AVAILABLE
|
|
||||||
extern pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Thread::launchThread()
|
|
||||||
{
|
|
||||||
#if JUCE_ANDROID
|
|
||||||
if (isAndroidRealtimeThread)
|
|
||||||
{
|
|
||||||
#if JUCE_ANDROID_REALTIME_THREAD_AVAILABLE
|
|
||||||
threadHandle = (void*) juce_createRealtimeAudioThread (threadEntryProc, this);
|
|
||||||
threadId = (ThreadID) threadHandle.get();
|
|
||||||
|
|
||||||
return;
|
|
||||||
#else
|
|
||||||
jassertfalse;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
threadHandle = {};
|
|
||||||
pthread_t handle = {};
|
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
pthread_attr_t* attrPtr = nullptr;
|
bool valid { pthread_attr_init (&attr) == 0 };
|
||||||
|
};
|
||||||
|
|
||||||
if (pthread_attr_init (&attr) == 0)
|
class PosixSchedulerPriority
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static PosixSchedulerPriority findCurrentSchedulerAndPriority()
|
||||||
{
|
{
|
||||||
attrPtr = &attr;
|
int scheduler{};
|
||||||
pthread_attr_setstacksize (attrPtr, threadStackSize);
|
sched_param param{};
|
||||||
|
pthread_getschedparam (pthread_self(), &scheduler, ¶m);
|
||||||
|
return { scheduler, param.sched_priority };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PosixSchedulerPriority getNativeSchedulerAndPriority (const Optional<Thread::RealtimeOptions>& rt,
|
||||||
if (pthread_create (&handle, attrPtr, threadEntryProc, this) == 0)
|
[[maybe_unused]] Thread::Priority prio)
|
||||||
{
|
{
|
||||||
pthread_detach (handle);
|
const auto isRealtime = rt.hasValue();
|
||||||
threadHandle = (void*) handle;
|
|
||||||
threadId = (ThreadID) threadHandle.get();
|
const auto priority = [&]
|
||||||
|
{
|
||||||
|
if (isRealtime)
|
||||||
|
{
|
||||||
|
const auto min = jmax (0, sched_get_priority_min (SCHED_RR));
|
||||||
|
const auto max = jmax (1, sched_get_priority_max (SCHED_RR));
|
||||||
|
|
||||||
|
return jmap (rt->priority, 0, 10, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only use this helper if we're on an old macos/ios platform that might
|
||||||
|
// still respect legacy pthread priorities for SCHED_OTHER.
|
||||||
|
#if JUCE_MAC || JUCE_IOS
|
||||||
|
const auto min = jmax (0, sched_get_priority_min (SCHED_OTHER));
|
||||||
|
const auto max = jmax (0, sched_get_priority_max (SCHED_OTHER));
|
||||||
|
|
||||||
|
const auto p = [prio]
|
||||||
|
{
|
||||||
|
switch (prio)
|
||||||
|
{
|
||||||
|
case Thread::Priority::highest: return 4;
|
||||||
|
case Thread::Priority::high: return 3;
|
||||||
|
case Thread::Priority::normal: return 2;
|
||||||
|
case Thread::Priority::low: return 1;
|
||||||
|
case Thread::Priority::background: return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (min != 0 && max != 0)
|
||||||
|
return jmap (p, 0, 4, min, max);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}();
|
||||||
|
|
||||||
|
#if JUCE_MAC || JUCE_IOS
|
||||||
|
const auto scheduler = SCHED_OTHER;
|
||||||
|
#elif JUCE_LINUX || JUCE_BSD
|
||||||
|
const auto backgroundSched = prio == Thread::Priority::background ? SCHED_IDLE
|
||||||
|
: SCHED_OTHER;
|
||||||
|
const auto scheduler = isRealtime ? SCHED_RR : backgroundSched;
|
||||||
|
#else
|
||||||
|
const auto scheduler = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return { scheduler, priority };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrPtr != nullptr)
|
void apply ([[maybe_unused]] PosixThreadAttribute& attr) const
|
||||||
pthread_attr_destroy (attrPtr);
|
{
|
||||||
|
#if JUCE_LINUX || JUCE_BSD
|
||||||
|
const struct sched_param param { getPriority() };
|
||||||
|
|
||||||
|
pthread_attr_setinheritsched (attr.get(), PTHREAD_EXPLICIT_SCHED);
|
||||||
|
pthread_attr_setschedpolicy (attr.get(), getScheduler());
|
||||||
|
pthread_attr_setschedparam (attr.get(), ¶m);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int getScheduler() const { return scheduler; }
|
||||||
|
constexpr int getPriority() const { return priority; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr PosixSchedulerPriority (int schedulerIn, int priorityIn)
|
||||||
|
: scheduler (schedulerIn), priority (priorityIn) {}
|
||||||
|
|
||||||
|
int scheduler;
|
||||||
|
int priority;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void* makeThreadHandle (PosixThreadAttribute& attr, Thread* userData, void* (*threadEntryProc) (void*))
|
||||||
|
{
|
||||||
|
pthread_t handle = {};
|
||||||
|
|
||||||
|
if (pthread_create (&handle, attr.get(), threadEntryProc, userData) != 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
pthread_detach (handle);
|
||||||
|
return (void*) handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::closeThreadHandle()
|
void Thread::closeThreadHandle()
|
||||||
{
|
{
|
||||||
threadId = {};
|
threadId = {};
|
||||||
threadHandle = {};
|
threadHandle = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
void Thread::killThread()
|
|
||||||
{
|
|
||||||
if (threadHandle.get() != nullptr)
|
|
||||||
{
|
|
||||||
#if JUCE_ANDROID
|
|
||||||
jassertfalse; // pthread_cancel not available!
|
|
||||||
#else
|
|
||||||
pthread_cancel ((pthread_t) threadHandle.get());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
|
void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
|
||||||
|
|
@ -963,41 +996,6 @@ void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::setThreadPriority (void* handle, int priority)
|
|
||||||
{
|
|
||||||
constexpr auto maxInputPriority = 10;
|
|
||||||
|
|
||||||
#if JUCE_LINUX || JUCE_BSD
|
|
||||||
constexpr auto lowestRrPriority = 8;
|
|
||||||
#else
|
|
||||||
constexpr auto lowestRrPriority = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct sched_param param;
|
|
||||||
int policy;
|
|
||||||
|
|
||||||
if (handle == nullptr)
|
|
||||||
handle = (void*) pthread_self();
|
|
||||||
|
|
||||||
if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
policy = priority < lowestRrPriority ? SCHED_OTHER : SCHED_RR;
|
|
||||||
|
|
||||||
const auto minPriority = sched_get_priority_min (policy);
|
|
||||||
const auto maxPriority = sched_get_priority_max (policy);
|
|
||||||
|
|
||||||
param.sched_priority = [&]
|
|
||||||
{
|
|
||||||
if (policy == SCHED_OTHER)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return jmap (priority, lowestRrPriority, maxInputPriority, minPriority, maxPriority);
|
|
||||||
}();
|
|
||||||
|
|
||||||
return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId()
|
Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId()
|
||||||
{
|
{
|
||||||
return (ThreadID) pthread_self();
|
return (ThreadID) pthread_self();
|
||||||
|
|
@ -1262,145 +1260,4 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
struct HighResolutionTimer::Pimpl
|
|
||||||
{
|
|
||||||
explicit Pimpl (HighResolutionTimer& t)
|
|
||||||
: owner (t)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~Pimpl()
|
|
||||||
{
|
|
||||||
jassert (periodMs == 0);
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void start (int newPeriod)
|
|
||||||
{
|
|
||||||
if (periodMs == newPeriod)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (thread.get_id() == std::this_thread::get_id())
|
|
||||||
{
|
|
||||||
periodMs = newPeriod;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stop();
|
|
||||||
|
|
||||||
periodMs = newPeriod;
|
|
||||||
|
|
||||||
thread = std::thread ([this, newPeriod]
|
|
||||||
{
|
|
||||||
setThisThreadToRealtime ((uint64) newPeriod);
|
|
||||||
|
|
||||||
auto lastPeriod = periodMs.load();
|
|
||||||
Clock clock (lastPeriod);
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> unique_lock (timerMutex);
|
|
||||||
|
|
||||||
while (periodMs != 0)
|
|
||||||
{
|
|
||||||
clock.next();
|
|
||||||
while (periodMs != 0 && clock.wait (stopCond, unique_lock));
|
|
||||||
|
|
||||||
if (periodMs == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
owner.hiResTimerCallback();
|
|
||||||
|
|
||||||
auto nextPeriod = periodMs.load();
|
|
||||||
|
|
||||||
if (lastPeriod != nextPeriod)
|
|
||||||
{
|
|
||||||
lastPeriod = nextPeriod;
|
|
||||||
clock = Clock (lastPeriod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
periodMs = 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
periodMs = 0;
|
|
||||||
|
|
||||||
const auto thread_id = thread.get_id();
|
|
||||||
|
|
||||||
if (thread_id == std::thread::id() || thread_id == std::this_thread::get_id())
|
|
||||||
return;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> unique_lock (timerMutex);
|
|
||||||
stopCond.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
HighResolutionTimer& owner;
|
|
||||||
std::atomic<int> periodMs { 0 };
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::thread thread;
|
|
||||||
std::condition_variable stopCond;
|
|
||||||
std::mutex timerMutex;
|
|
||||||
|
|
||||||
class Clock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Clock (std::chrono::steady_clock::rep millis) noexcept
|
|
||||||
: time (std::chrono::steady_clock::now()),
|
|
||||||
delta (std::chrono::milliseconds (millis))
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool wait (std::condition_variable& cond, std::unique_lock<std::mutex>& lock) noexcept
|
|
||||||
{
|
|
||||||
return cond.wait_until (lock, time) != std::cv_status::timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
void next() noexcept
|
|
||||||
{
|
|
||||||
time += delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::chrono::time_point<std::chrono::steady_clock> time;
|
|
||||||
std::chrono::steady_clock::duration delta;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool setThisThreadToRealtime (uint64 periodMs)
|
|
||||||
{
|
|
||||||
const auto thread = pthread_self();
|
|
||||||
|
|
||||||
#if JUCE_MAC || JUCE_IOS
|
|
||||||
mach_timebase_info_data_t timebase;
|
|
||||||
mach_timebase_info (&timebase);
|
|
||||||
|
|
||||||
const auto ticksPerMs = ((double) timebase.denom * 1000000.0) / (double) timebase.numer;
|
|
||||||
const auto periodTicks = (uint32_t) jmin ((double) std::numeric_limits<uint32_t>::max(), periodMs * ticksPerMs);
|
|
||||||
|
|
||||||
thread_time_constraint_policy_data_t policy;
|
|
||||||
policy.period = periodTicks;
|
|
||||||
policy.computation = jmin ((uint32_t) 50000, policy.period);
|
|
||||||
policy.constraint = policy.period;
|
|
||||||
policy.preemptible = true;
|
|
||||||
|
|
||||||
return thread_policy_set (pthread_mach_thread_np (thread),
|
|
||||||
THREAD_TIME_CONSTRAINT_POLICY,
|
|
||||||
(thread_policy_t) &policy,
|
|
||||||
THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS;
|
|
||||||
|
|
||||||
#else
|
|
||||||
ignoreUnused (periodMs);
|
|
||||||
struct sched_param param;
|
|
||||||
param.sched_priority = sched_get_priority_max (SCHED_RR);
|
|
||||||
return pthread_setschedparam (thread, SCHED_RR, ¶m) == 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace juce
|
} // namespace juce
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRI
|
||||||
bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) &lock) != FALSE; }
|
bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) &lock) != FALSE; }
|
||||||
void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) &lock); }
|
void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) &lock); }
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
static unsigned int STDMETHODCALLTYPE threadEntryProc (void* userData)
|
static unsigned int STDMETHODCALLTYPE threadEntryProc (void* userData)
|
||||||
{
|
{
|
||||||
|
|
@ -65,31 +64,72 @@ static unsigned int STDMETHODCALLTYPE threadEntryProc (void* userData)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::launchThread()
|
static bool setPriorityInternal (bool isRealtime, HANDLE handle, Thread::Priority priority)
|
||||||
|
{
|
||||||
|
auto nativeThreadFlag = isRealtime ? THREAD_PRIORITY_TIME_CRITICAL
|
||||||
|
: ThreadPriorities::getNativePriority (priority);
|
||||||
|
|
||||||
|
if (isRealtime) // This should probably be a fail state too?
|
||||||
|
Process::setPriority (Process::ProcessPriority::RealtimePriority);
|
||||||
|
|
||||||
|
return SetThreadPriority (handle, nativeThreadFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::createNativeThread (Priority priority)
|
||||||
{
|
{
|
||||||
unsigned int newThreadId;
|
unsigned int newThreadId;
|
||||||
threadHandle = (void*) _beginthreadex (nullptr, (unsigned int) threadStackSize,
|
threadHandle = (void*) _beginthreadex (nullptr, (unsigned int) threadStackSize,
|
||||||
&threadEntryProc, this, 0, &newThreadId);
|
&threadEntryProc, this, CREATE_SUSPENDED,
|
||||||
threadId = (ThreadID) (pointer_sized_int) newThreadId;
|
&newThreadId);
|
||||||
|
|
||||||
|
if (threadHandle != nullptr)
|
||||||
|
{
|
||||||
|
threadId = (ThreadID) (pointer_sized_int) newThreadId;
|
||||||
|
|
||||||
|
if (setPriorityInternal (isRealtime(), threadHandle, priority))
|
||||||
|
{
|
||||||
|
ResumeThread (threadHandle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
killThread();
|
||||||
|
closeThreadHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread::Priority Thread::getPriority() const
|
||||||
|
{
|
||||||
|
jassert (Thread::getCurrentThreadId() == getThreadId());
|
||||||
|
|
||||||
|
const auto native = GetThreadPriority (threadHandle);
|
||||||
|
return ThreadPriorities::getJucePriority (native);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::setPriority (Priority priority)
|
||||||
|
{
|
||||||
|
jassert (Thread::getCurrentThreadId() == getThreadId());
|
||||||
|
return setPriorityInternal (isRealtime(), this, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::closeThreadHandle()
|
void Thread::closeThreadHandle()
|
||||||
{
|
{
|
||||||
CloseHandle ((HANDLE) threadHandle.get());
|
CloseHandle (threadHandle);
|
||||||
threadId = nullptr;
|
threadId = nullptr;
|
||||||
threadHandle = nullptr;
|
threadHandle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::killThread()
|
void Thread::killThread()
|
||||||
{
|
{
|
||||||
if (threadHandle.get() != nullptr)
|
if (threadHandle != nullptr)
|
||||||
{
|
{
|
||||||
#if JUCE_DEBUG
|
#if JUCE_DEBUG
|
||||||
OutputDebugStringA ("** Warning - Forced thread termination **\n");
|
OutputDebugStringA ("** Warning - Forced thread termination **\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6258)
|
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6258)
|
||||||
TerminateThread (threadHandle.get(), 0);
|
TerminateThread (threadHandle, 0);
|
||||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -129,23 +169,6 @@ Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId()
|
||||||
return (ThreadID) (pointer_sized_int) GetCurrentThreadId();
|
return (ThreadID) (pointer_sized_int) GetCurrentThreadId();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::setThreadPriority (void* handle, int priority)
|
|
||||||
{
|
|
||||||
int pri = THREAD_PRIORITY_TIME_CRITICAL;
|
|
||||||
|
|
||||||
if (priority < 1) pri = THREAD_PRIORITY_IDLE;
|
|
||||||
else if (priority < 2) pri = THREAD_PRIORITY_LOWEST;
|
|
||||||
else if (priority < 5) pri = THREAD_PRIORITY_BELOW_NORMAL;
|
|
||||||
else if (priority < 7) pri = THREAD_PRIORITY_NORMAL;
|
|
||||||
else if (priority < 9) pri = THREAD_PRIORITY_ABOVE_NORMAL;
|
|
||||||
else if (priority < 10) pri = THREAD_PRIORITY_HIGHEST;
|
|
||||||
|
|
||||||
if (handle == nullptr)
|
|
||||||
handle = GetCurrentThread();
|
|
||||||
|
|
||||||
return SetThreadPriority (handle, pri) != FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
|
void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
|
||||||
{
|
{
|
||||||
SetThreadAffinityMask (GetCurrentThread(), affinityMask);
|
SetThreadAffinityMask (GetCurrentThread(), affinityMask);
|
||||||
|
|
@ -217,11 +240,11 @@ void juce_repeatLastProcessPriority()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior)
|
void JUCE_CALLTYPE Process::setPriority (ProcessPriority newPriority)
|
||||||
{
|
{
|
||||||
if (lastProcessPriority != (int) prior)
|
if (lastProcessPriority != (int) newPriority)
|
||||||
{
|
{
|
||||||
lastProcessPriority = (int) prior;
|
lastProcessPriority = (int) newPriority;
|
||||||
juce_repeatLastProcessPriority();
|
juce_repeatLastProcessPriority();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -527,56 +550,4 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
|
||||||
return start (escaped.trim(), streamFlags);
|
return start (escaped.trim(), streamFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
struct HighResolutionTimer::Pimpl
|
|
||||||
{
|
|
||||||
Pimpl (HighResolutionTimer& t) noexcept : owner (t)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~Pimpl()
|
|
||||||
{
|
|
||||||
jassert (periodMs == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void start (int newPeriod)
|
|
||||||
{
|
|
||||||
if (newPeriod != periodMs)
|
|
||||||
{
|
|
||||||
stop();
|
|
||||||
periodMs = newPeriod;
|
|
||||||
|
|
||||||
TIMECAPS tc;
|
|
||||||
if (timeGetDevCaps (&tc, sizeof (tc)) == TIMERR_NOERROR)
|
|
||||||
{
|
|
||||||
const int actualPeriod = jlimit ((int) tc.wPeriodMin, (int) tc.wPeriodMax, newPeriod);
|
|
||||||
|
|
||||||
timerID = timeSetEvent ((UINT) actualPeriod, tc.wPeriodMin, callbackFunction, (DWORD_PTR) this,
|
|
||||||
TIME_PERIODIC | TIME_CALLBACK_FUNCTION | 0x100 /*TIME_KILL_SYNCHRONOUS*/);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
periodMs = 0;
|
|
||||||
timeKillEvent (timerID);
|
|
||||||
}
|
|
||||||
|
|
||||||
HighResolutionTimer& owner;
|
|
||||||
int periodMs = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
unsigned int timerID;
|
|
||||||
|
|
||||||
static void STDMETHODCALLTYPE callbackFunction (UINT, UINT, DWORD_PTR userInfo, DWORD_PTR, DWORD_PTR)
|
|
||||||
{
|
|
||||||
if (Pimpl* const timer = reinterpret_cast<Pimpl*> (userInfo))
|
|
||||||
if (timer->periodMs != 0)
|
|
||||||
timer->owner.hiResTimerCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace juce
|
} // namespace juce
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,6 @@ public:
|
||||||
*/
|
*/
|
||||||
static bool isRunningInAppExtensionSandbox() noexcept;
|
static bool isRunningInAppExtensionSandbox() noexcept;
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#ifndef DOXYGEN
|
#ifndef DOXYGEN
|
||||||
[[deprecated ("This method was spelt wrong! Please change your code to use getCpuSpeedInMegahertz instead.")]]
|
[[deprecated ("This method was spelt wrong! Please change your code to use getCpuSpeedInMegahertz instead.")]]
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,96 @@
|
||||||
namespace juce
|
namespace juce
|
||||||
{
|
{
|
||||||
|
|
||||||
HighResolutionTimer::HighResolutionTimer() : pimpl (new Pimpl (*this)) {}
|
class HighResolutionTimer::Pimpl : private Thread
|
||||||
HighResolutionTimer::~HighResolutionTimer() { stopTimer(); }
|
{
|
||||||
|
using steady_clock = std::chrono::steady_clock;
|
||||||
|
using milliseconds = std::chrono::milliseconds;
|
||||||
|
|
||||||
void HighResolutionTimer::startTimer (int periodMs) { pimpl->start (jmax (1, periodMs)); }
|
public:
|
||||||
void HighResolutionTimer::stopTimer() { pimpl->stop(); }
|
explicit Pimpl (HighResolutionTimer& ownerRef)
|
||||||
|
: Thread ("HighResolutionTimerThread"),
|
||||||
|
owner (ownerRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool HighResolutionTimer::isTimerRunning() const noexcept { return pimpl->periodMs != 0; }
|
using Thread::isThreadRunning;
|
||||||
int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->periodMs; }
|
|
||||||
|
void start (int periodMs)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const std::scoped_lock lk { mutex };
|
||||||
|
periodMillis = periodMs;
|
||||||
|
nextTickTime = steady_clock::now() + milliseconds (periodMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! isThreadRunning())
|
||||||
|
startThread (Thread::Priority::high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const std::scoped_lock lk { mutex };
|
||||||
|
periodMillis = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
waitEvent.notify_one();
|
||||||
|
|
||||||
|
if (Thread::getCurrentThreadId() != getThreadId())
|
||||||
|
stopThread (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPeriod() const
|
||||||
|
{
|
||||||
|
return periodMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock lk { mutex };
|
||||||
|
|
||||||
|
if (waitEvent.wait_until (lk, nextTickTime, [this] { return periodMillis == 0; }))
|
||||||
|
break;
|
||||||
|
|
||||||
|
nextTickTime = steady_clock::now() + milliseconds (periodMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
owner.hiResTimerCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HighResolutionTimer& owner;
|
||||||
|
std::atomic<int> periodMillis { 0 };
|
||||||
|
steady_clock::time_point nextTickTime;
|
||||||
|
std::mutex mutex;
|
||||||
|
std::condition_variable waitEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
HighResolutionTimer::HighResolutionTimer()
|
||||||
|
: pimpl (new Pimpl (*this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HighResolutionTimer::~HighResolutionTimer()
|
||||||
|
{
|
||||||
|
stopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighResolutionTimer::startTimer (int periodMs)
|
||||||
|
{
|
||||||
|
pimpl->start (jmax (1, periodMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighResolutionTimer::stopTimer()
|
||||||
|
{
|
||||||
|
pimpl->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighResolutionTimer::isTimerRunning() const noexcept { return getTimerInterval() != 0; }
|
||||||
|
int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->getPeriod(); }
|
||||||
|
|
||||||
} // namespace juce
|
} // namespace juce
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ public:
|
||||||
int getTimerInterval() const noexcept;
|
int getTimerInterval() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Pimpl;
|
class Pimpl;
|
||||||
std::unique_ptr<Pimpl> pimpl;
|
std::unique_ptr<Pimpl> pimpl;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer)
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ public:
|
||||||
@param priority the process priority, where
|
@param priority the process priority, where
|
||||||
0=low, 1=normal, 2=high, 3=realtime
|
0=low, 1=normal, 2=high, 3=realtime
|
||||||
*/
|
*/
|
||||||
static void JUCE_CALLTYPE setPriority (const ProcessPriority priority);
|
static void JUCE_CALLTYPE setPriority (ProcessPriority priority);
|
||||||
|
|
||||||
/** Kills the current process immediately.
|
/** Kills the current process immediately.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@
|
||||||
|
|
||||||
namespace juce
|
namespace juce
|
||||||
{
|
{
|
||||||
|
//==============================================================================
|
||||||
Thread::Thread (const String& name, size_t stackSize)
|
Thread::Thread (const String& name, size_t stackSize) : threadName (name),
|
||||||
: threadName (name), threadStackSize (stackSize)
|
threadStackSize (stackSize)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,12 +81,18 @@ void Thread::threadEntryPoint()
|
||||||
const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
|
const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
|
||||||
currentThreadHolder->value = this;
|
currentThreadHolder->value = this;
|
||||||
|
|
||||||
|
#if JUCE_ANDROID
|
||||||
|
setPriority (priority);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (threadName.isNotEmpty())
|
if (threadName.isNotEmpty())
|
||||||
setCurrentThreadName (threadName);
|
setCurrentThreadName (threadName);
|
||||||
|
|
||||||
|
// This 'startSuspensionEvent' protects 'threadId' which is initialised after the platform's native 'CreateThread' method.
|
||||||
|
// This ensures it has been initialised correctly before it reaches this point.
|
||||||
if (startSuspensionEvent.wait (10000))
|
if (startSuspensionEvent.wait (10000))
|
||||||
{
|
{
|
||||||
jassert (getCurrentThreadId() == threadId.get());
|
jassert (getCurrentThreadId() == threadId);
|
||||||
|
|
||||||
if (affinityMask != 0)
|
if (affinityMask != 0)
|
||||||
setCurrentThreadAffinityMask (affinityMask);
|
setCurrentThreadAffinityMask (affinityMask);
|
||||||
|
|
@ -119,42 +125,65 @@ void JUCE_API juce_threadEntryPoint (void* userData)
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void Thread::startThread()
|
bool Thread::startThreadInternal (Priority threadPriority)
|
||||||
{
|
{
|
||||||
const ScopedLock sl (startStopLock);
|
shouldExit = false;
|
||||||
|
|
||||||
shouldExit = 0;
|
// 'priority' is essentially useless on Linux as only realtime
|
||||||
|
// has any options but we need to set this here to satsify
|
||||||
|
// later queries, otherwise we get inconsistent results across
|
||||||
|
// platforms.
|
||||||
|
#if JUCE_LINUX || JUCE_BSD
|
||||||
|
priority = threadPriority;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (threadHandle.get() == nullptr)
|
if (createNativeThread (threadPriority))
|
||||||
{
|
{
|
||||||
launchThread();
|
|
||||||
setThreadPriority (threadHandle.get(), threadPriority);
|
|
||||||
startSuspensionEvent.signal();
|
startSuspensionEvent.signal();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::startThread (int priority)
|
bool Thread::startThread()
|
||||||
|
{
|
||||||
|
return startThread (Priority::normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::startThread (Priority threadPriority)
|
||||||
{
|
{
|
||||||
const ScopedLock sl (startStopLock);
|
const ScopedLock sl (startStopLock);
|
||||||
|
|
||||||
if (threadHandle.get() == nullptr)
|
if (threadHandle == nullptr)
|
||||||
{
|
{
|
||||||
#if JUCE_ANDROID
|
realtimeOptions.reset();
|
||||||
isAndroidRealtimeThread = (priority == realtimeAudioPriority);
|
return startThreadInternal (threadPriority);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
threadPriority = getAdjustedPriority (priority);
|
return false;
|
||||||
startThread();
|
}
|
||||||
}
|
|
||||||
else
|
bool Thread::startRealtimeThread (const RealtimeOptions& options)
|
||||||
|
{
|
||||||
|
const ScopedLock sl (startStopLock);
|
||||||
|
|
||||||
|
if (threadHandle == nullptr)
|
||||||
{
|
{
|
||||||
setPriority (priority);
|
realtimeOptions = makeOptional (options);
|
||||||
|
|
||||||
|
if (startThreadInternal (Priority::normal))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
realtimeOptions.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::isThreadRunning() const
|
bool Thread::isThreadRunning() const
|
||||||
{
|
{
|
||||||
return threadHandle.get() != nullptr;
|
return threadHandle != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread* JUCE_CALLTYPE Thread::getCurrentThread()
|
Thread* JUCE_CALLTYPE Thread::getCurrentThread()
|
||||||
|
|
@ -164,19 +193,19 @@ Thread* JUCE_CALLTYPE Thread::getCurrentThread()
|
||||||
|
|
||||||
Thread::ThreadID Thread::getThreadId() const noexcept
|
Thread::ThreadID Thread::getThreadId() const noexcept
|
||||||
{
|
{
|
||||||
return threadId.get();
|
return threadId;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void Thread::signalThreadShouldExit()
|
void Thread::signalThreadShouldExit()
|
||||||
{
|
{
|
||||||
shouldExit = 1;
|
shouldExit = true;
|
||||||
listeners.call ([] (Listener& l) { l.exitSignalSent(); });
|
listeners.call ([] (Listener& l) { l.exitSignalSent(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::threadShouldExit() const
|
bool Thread::threadShouldExit() const
|
||||||
{
|
{
|
||||||
return shouldExit.get() != 0;
|
return shouldExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::currentThreadShouldExit()
|
bool Thread::currentThreadShouldExit()
|
||||||
|
|
@ -249,40 +278,9 @@ void Thread::removeListener (Listener* listener)
|
||||||
listeners.remove (listener);
|
listeners.remove (listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
bool Thread::isRealtime() const
|
||||||
bool Thread::setPriority (int newPriority)
|
|
||||||
{
|
{
|
||||||
newPriority = getAdjustedPriority (newPriority);
|
return realtimeOptions.hasValue();
|
||||||
|
|
||||||
// NB: deadlock possible if you try to set the thread prio from the thread itself,
|
|
||||||
// so using setCurrentThreadPriority instead in that case.
|
|
||||||
if (getCurrentThreadId() == getThreadId())
|
|
||||||
return setCurrentThreadPriority (newPriority);
|
|
||||||
|
|
||||||
const ScopedLock sl (startStopLock);
|
|
||||||
|
|
||||||
#if JUCE_ANDROID
|
|
||||||
bool isRealtime = (newPriority == realtimeAudioPriority);
|
|
||||||
|
|
||||||
// you cannot switch from or to an Android realtime thread once the
|
|
||||||
// thread is already running!
|
|
||||||
jassert (isThreadRunning() && (isRealtime == isAndroidRealtimeThread));
|
|
||||||
|
|
||||||
isAndroidRealtimeThread = isRealtime;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((! isThreadRunning()) || setThreadPriority (threadHandle.get(), newPriority))
|
|
||||||
{
|
|
||||||
threadPriority = newPriority;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Thread::setCurrentThreadPriority (const int newPriority)
|
|
||||||
{
|
|
||||||
return setThreadPriority ({}, getAdjustedPriority (newPriority));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::setAffinityMask (const uint32 newAffinityMask)
|
void Thread::setAffinityMask (const uint32 newAffinityMask)
|
||||||
|
|
@ -290,11 +288,6 @@ void Thread::setAffinityMask (const uint32 newAffinityMask)
|
||||||
affinityMask = newAffinityMask;
|
affinityMask = newAffinityMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Thread::getAdjustedPriority (int newPriority)
|
|
||||||
{
|
|
||||||
return jlimit (0, 10, newPriority == realtimeAudioPriority ? 9 : newPriority);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
bool Thread::wait (const int timeOutMilliseconds) const
|
bool Thread::wait (const int timeOutMilliseconds) const
|
||||||
{
|
{
|
||||||
|
|
@ -349,7 +342,6 @@ bool JUCE_CALLTYPE Process::isRunningUnderDebugger() noexcept
|
||||||
return juce_isRunningUnderDebugger();
|
return juce_isRunningUnderDebugger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#if JUCE_UNIT_TESTS
|
#if JUCE_UNIT_TESTS
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ namespace juce
|
||||||
Encapsulates a thread.
|
Encapsulates a thread.
|
||||||
|
|
||||||
Subclasses derive from Thread and implement the run() method, in which they
|
Subclasses derive from Thread and implement the run() method, in which they
|
||||||
do their business. The thread can then be started with the startThread() method
|
do their business. The thread can then be started with the startThread() or
|
||||||
and controlled with various other methods.
|
startRealtimeThread() methods and controlled with various other methods.
|
||||||
|
|
||||||
This class also contains some thread-related static methods, such
|
This class also contains some thread-related static methods, such
|
||||||
as sleep(), yield(), getCurrentThreadId() etc.
|
as sleep(), yield(), getCurrentThreadId() etc.
|
||||||
|
|
@ -42,6 +42,46 @@ namespace juce
|
||||||
class JUCE_API Thread
|
class JUCE_API Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
//==============================================================================
|
||||||
|
/** The different runtime priorities of non-realtime threads.
|
||||||
|
|
||||||
|
@see startThread
|
||||||
|
*/
|
||||||
|
enum class Priority
|
||||||
|
{
|
||||||
|
/** The highest possible priority that isn't a dedicated realtime thread. */
|
||||||
|
highest,
|
||||||
|
|
||||||
|
/** Makes use of performance cores and higher clocks. */
|
||||||
|
high,
|
||||||
|
|
||||||
|
/** The OS default. It will balance out across all cores. */
|
||||||
|
normal,
|
||||||
|
|
||||||
|
/** Uses efficiency cores when possible. */
|
||||||
|
low,
|
||||||
|
|
||||||
|
/** Restricted to efficiency cores on platforms that have them. */
|
||||||
|
background
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** A selection of options available when creating realtime threads.
|
||||||
|
|
||||||
|
@see startRealtimeThread
|
||||||
|
*/
|
||||||
|
struct RealtimeOptions
|
||||||
|
{
|
||||||
|
/** Linux only: A value with a range of 0-10, where 10 is the highest priority. */
|
||||||
|
int priority = 5;
|
||||||
|
|
||||||
|
/* iOS/macOS only: A millisecond value representing the estimated time between each
|
||||||
|
'Thread::run' call. Your thread may be penalised if you frequently
|
||||||
|
overrun this.
|
||||||
|
*/
|
||||||
|
uint32_t workDurationMs = 0;
|
||||||
|
};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/**
|
/**
|
||||||
Creates a thread.
|
Creates a thread.
|
||||||
|
|
@ -78,23 +118,51 @@ public:
|
||||||
virtual void run() = 0;
|
virtual void run() = 0;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** Starts the thread running.
|
/** Attempts to start a new thread with default ('Priority::normal') priority.
|
||||||
|
|
||||||
This will cause the thread's run() method to be called by a new thread.
|
This will cause the thread's run() method to be called by a new thread.
|
||||||
If this thread is already running, startThread() won't do anything.
|
If this thread is already running, startThread() won't do anything.
|
||||||
|
|
||||||
|
If a thread cannot be created with the requested priority, this will return false
|
||||||
|
and Thread::run() will not be called. An exception to this is the Android platform,
|
||||||
|
which always starts a thread and attempts to upgrade the thread after creation.
|
||||||
|
|
||||||
|
@returns true if the thread started successfully. false if it was unsuccesful.
|
||||||
|
|
||||||
@see stopThread
|
@see stopThread
|
||||||
*/
|
*/
|
||||||
void startThread();
|
bool startThread();
|
||||||
|
|
||||||
/** Starts the thread with a given priority.
|
/** Attempts to start a new thread with a given priority.
|
||||||
|
|
||||||
Launches the thread with a given priority, where 0 = lowest, 10 = highest.
|
This will cause the thread's run() method to be called by a new thread.
|
||||||
If the thread is already running, its priority will be changed.
|
If this thread is already running, startThread() won't do anything.
|
||||||
|
|
||||||
@see startThread, setPriority, realtimeAudioPriority
|
If a thread cannot be created with the requested priority, this will return false
|
||||||
|
and Thread::run() will not be called. An exception to this is the Android platform,
|
||||||
|
which always starts a thread and attempts to upgrade the thread after creation.
|
||||||
|
|
||||||
|
@param newPriority Priority the thread should be assigned. This parameter is ignored
|
||||||
|
on Linux.
|
||||||
|
|
||||||
|
@returns true if the thread started successfully, false if it was unsuccesful.
|
||||||
|
|
||||||
|
@see startThread, setPriority, startRealtimeThread
|
||||||
*/
|
*/
|
||||||
void startThread (int priority);
|
bool startThread (Priority newPriority);
|
||||||
|
|
||||||
|
/** Starts the thread with realtime performance characteristics on platforms
|
||||||
|
that support it.
|
||||||
|
|
||||||
|
You cannot change the options of a running realtime thread, nor switch
|
||||||
|
a non-realtime thread to a realtime thread. To make these changes you must
|
||||||
|
first stop the thread and then restart with different options.
|
||||||
|
|
||||||
|
@param options Realtime options the thread should be created with.
|
||||||
|
|
||||||
|
@see startThread, RealtimeOptions
|
||||||
|
*/
|
||||||
|
bool startRealtimeThread (const RealtimeOptions& options);
|
||||||
|
|
||||||
/** Attempts to stop the thread running.
|
/** Attempts to stop the thread running.
|
||||||
|
|
||||||
|
|
@ -198,50 +266,8 @@ public:
|
||||||
/** Removes a listener added with addListener. */
|
/** Removes a listener added with addListener. */
|
||||||
void removeListener (Listener*);
|
void removeListener (Listener*);
|
||||||
|
|
||||||
//==============================================================================
|
/** Returns true if this Thread represents a realtime thread. */
|
||||||
/** Special realtime audio thread priority
|
bool isRealtime() const;
|
||||||
|
|
||||||
This priority will create a high-priority thread which is best suited
|
|
||||||
for realtime audio processing.
|
|
||||||
|
|
||||||
Currently, this priority is identical to priority 9, except when building
|
|
||||||
for Android with OpenSL/Oboe support.
|
|
||||||
|
|
||||||
In this case, JUCE will ask OpenSL/Oboe to construct a super high priority thread
|
|
||||||
specifically for realtime audio processing.
|
|
||||||
|
|
||||||
Note that this priority can only be set **before** the thread has
|
|
||||||
started. Switching to this priority, or from this priority to a different
|
|
||||||
priority, is not supported under Android and will assert.
|
|
||||||
|
|
||||||
For best performance this thread should yield at regular intervals
|
|
||||||
and not call any blocking APIs.
|
|
||||||
|
|
||||||
@see startThread, setPriority, sleep, WaitableEvent
|
|
||||||
*/
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
realtimeAudioPriority = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Changes the thread's priority.
|
|
||||||
|
|
||||||
May return false if for some reason the priority can't be changed.
|
|
||||||
|
|
||||||
@param priority the new priority, in the range 0 (lowest) to 10 (highest). A priority
|
|
||||||
of 5 is normal.
|
|
||||||
@see realtimeAudioPriority
|
|
||||||
*/
|
|
||||||
bool setPriority (int priority);
|
|
||||||
|
|
||||||
/** Changes the priority of the caller thread.
|
|
||||||
|
|
||||||
Similar to setPriority(), but this static method acts on the caller thread.
|
|
||||||
May return false if for some reason the priority can't be changed.
|
|
||||||
|
|
||||||
@see setPriority
|
|
||||||
*/
|
|
||||||
static bool setCurrentThreadPriority (int priority);
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** Sets the affinity mask for the thread.
|
/** Sets the affinity mask for the thread.
|
||||||
|
|
@ -380,34 +406,58 @@ public:
|
||||||
static void initialiseJUCE (void* jniEnv, void* jContext);
|
static void initialiseJUCE (void* jniEnv, void* jContext);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//==============================================================================
|
||||||
|
/** Returns the current priority of this thread.
|
||||||
|
|
||||||
|
This can only be called from the target thread. Doing so from another thread
|
||||||
|
will cause an assert.
|
||||||
|
|
||||||
|
@see setPriority
|
||||||
|
*/
|
||||||
|
Priority getPriority() const;
|
||||||
|
|
||||||
|
/** Attempts to set the priority for this thread. Returns true if the new priority
|
||||||
|
was set successfully, false if not.
|
||||||
|
|
||||||
|
This can only be called from the target thread. Doing so from another thread
|
||||||
|
will cause an assert.
|
||||||
|
|
||||||
|
@param newPriority The new priority to be applied to the thread. Note: This
|
||||||
|
has no effect on Linux platforms, subsequent calls to
|
||||||
|
'getPriority' will return this value.
|
||||||
|
|
||||||
|
@see Priority
|
||||||
|
*/
|
||||||
|
bool setPriority (Priority newPriority);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
const String threadName;
|
const String threadName;
|
||||||
Atomic<void*> threadHandle { nullptr };
|
std::atomic<void*> threadHandle { nullptr };
|
||||||
Atomic<ThreadID> threadId = {};
|
std::atomic<ThreadID> threadId { nullptr };
|
||||||
|
Optional<RealtimeOptions> realtimeOptions = {};
|
||||||
CriticalSection startStopLock;
|
CriticalSection startStopLock;
|
||||||
WaitableEvent startSuspensionEvent, defaultEvent;
|
WaitableEvent startSuspensionEvent, defaultEvent;
|
||||||
int threadPriority = 5;
|
|
||||||
size_t threadStackSize;
|
size_t threadStackSize;
|
||||||
uint32 affinityMask = 0;
|
uint32 affinityMask = 0;
|
||||||
bool deleteOnThreadEnd = false;
|
bool deleteOnThreadEnd = false;
|
||||||
Atomic<int32> shouldExit { 0 };
|
std::atomic<bool> shouldExit { false };
|
||||||
ListenerList<Listener, Array<Listener*, CriticalSection>> listeners;
|
ListenerList<Listener, Array<Listener*, CriticalSection>> listeners;
|
||||||
|
|
||||||
#if JUCE_ANDROID
|
#if JUCE_ANDROID || JUCE_LINUX || JUCE_BSD
|
||||||
bool isAndroidRealtimeThread = false;
|
std::atomic<Priority> priority;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DOXYGEN
|
#ifndef DOXYGEN
|
||||||
friend void JUCE_API juce_threadEntryPoint (void*);
|
friend void JUCE_API juce_threadEntryPoint (void*);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void launchThread();
|
bool startThreadInternal (Priority);
|
||||||
|
bool createNativeThread (Priority);
|
||||||
void closeThreadHandle();
|
void closeThreadHandle();
|
||||||
void killThread();
|
void killThread();
|
||||||
void threadEntryPoint();
|
void threadEntryPoint();
|
||||||
static bool setThreadPriority (void*, int);
|
|
||||||
static int getAdjustedPriority (int);
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,14 @@ struct ThreadPool::ThreadPoolThread : public Thread
|
||||||
void run() override
|
void run() override
|
||||||
{
|
{
|
||||||
while (! threadShouldExit())
|
while (! threadShouldExit())
|
||||||
|
{
|
||||||
if (! pool.runNextJob (*this))
|
if (! pool.runNextJob (*this))
|
||||||
wait (500);
|
wait (500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<ThreadPoolJob*> currentJob { nullptr };
|
std::atomic<ThreadPoolJob*> currentJob { nullptr };
|
||||||
|
|
||||||
ThreadPool& pool;
|
ThreadPool& pool;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread)
|
||||||
|
|
@ -90,16 +93,19 @@ ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob()
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
ThreadPool::ThreadPool (int numThreads, size_t threadStackSize)
|
ThreadPool::ThreadPool (int numThreads, size_t threadStackSize, Thread::Priority priority)
|
||||||
{
|
{
|
||||||
jassert (numThreads > 0); // not much point having a pool without any threads!
|
jassert (numThreads > 0); // not much point having a pool without any threads!
|
||||||
|
|
||||||
createThreads (numThreads, threadStackSize);
|
for (int i = jmax (1, numThreads); --i >= 0;)
|
||||||
|
threads.add (new ThreadPoolThread (*this, threadStackSize));
|
||||||
|
|
||||||
|
for (auto* t : threads)
|
||||||
|
t->startThread (priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool::ThreadPool()
|
ThreadPool::ThreadPool() : ThreadPool (SystemStats::getNumCpus(), 0, Thread::Priority::normal)
|
||||||
{
|
{
|
||||||
createThreads (SystemStats::getNumCpus());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool::~ThreadPool()
|
ThreadPool::~ThreadPool()
|
||||||
|
|
@ -108,15 +114,6 @@ ThreadPool::~ThreadPool()
|
||||||
stopThreads();
|
stopThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::createThreads (int numThreads, size_t threadStackSize)
|
|
||||||
{
|
|
||||||
for (int i = jmax (1, numThreads); --i >= 0;)
|
|
||||||
threads.add (new ThreadPoolThread (*this, threadStackSize));
|
|
||||||
|
|
||||||
for (auto* t : threads)
|
|
||||||
t->startThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadPool::stopThreads()
|
void ThreadPool::stopThreads()
|
||||||
{
|
{
|
||||||
for (auto* t : threads)
|
for (auto* t : threads)
|
||||||
|
|
@ -330,17 +327,6 @@ StringArray ThreadPool::getNamesOfAllJobs (bool onlyReturnActiveJobs) const
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ThreadPool::setThreadPriorities (int newPriority)
|
|
||||||
{
|
|
||||||
bool ok = true;
|
|
||||||
|
|
||||||
for (auto* t : threads)
|
|
||||||
if (! t->setPriority (newPriority))
|
|
||||||
ok = false;
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadPoolJob* ThreadPool::pickNextJobToRun()
|
ThreadPoolJob* ThreadPool::pickNextJobToRun()
|
||||||
{
|
{
|
||||||
OwnedArray<ThreadPoolJob> deletionList;
|
OwnedArray<ThreadPoolJob> deletionList;
|
||||||
|
|
|
||||||
|
|
@ -164,8 +164,9 @@ public:
|
||||||
@param threadStackSize the size of the stack of each thread. If this value
|
@param threadStackSize the size of the stack of each thread. If this value
|
||||||
is zero then the default stack size of the OS will
|
is zero then the default stack size of the OS will
|
||||||
be used.
|
be used.
|
||||||
|
@param priority the desired priority of each thread in the pool.
|
||||||
*/
|
*/
|
||||||
ThreadPool (int numberOfThreads, size_t threadStackSize = 0);
|
ThreadPool (int numberOfThreads, size_t threadStackSize = 0, Thread::Priority priority = Thread::Priority::normal);
|
||||||
|
|
||||||
/** Creates a thread pool with one thread per CPU core.
|
/** Creates a thread pool with one thread per CPU core.
|
||||||
Once you've created a pool, you can give it some jobs by calling addJob().
|
Once you've created a pool, you can give it some jobs by calling addJob().
|
||||||
|
|
@ -309,13 +310,6 @@ public:
|
||||||
*/
|
*/
|
||||||
StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const;
|
StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const;
|
||||||
|
|
||||||
/** Changes the priority of all the threads.
|
|
||||||
This will call Thread::setPriority() for each thread in the pool.
|
|
||||||
May return false if for some reason the priority can't be changed.
|
|
||||||
*/
|
|
||||||
bool setThreadPriorities (int newPriority);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
Array<ThreadPoolJob*> jobs;
|
Array<ThreadPoolJob*> jobs;
|
||||||
|
|
@ -330,7 +324,6 @@ private:
|
||||||
bool runNextJob (ThreadPoolThread&);
|
bool runNextJob (ThreadPoolThread&);
|
||||||
ThreadPoolJob* pickNextJobToRun();
|
ThreadPoolJob* pickNextJobToRun();
|
||||||
void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
|
void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
|
||||||
void createThreads (int numThreads, size_t threadStackSize = 0);
|
|
||||||
void stopThreads();
|
void stopThreads();
|
||||||
|
|
||||||
// Note that this method has changed, and no longer has a parameter to indicate
|
// Note that this method has changed, and no longer has a parameter to indicate
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ struct ChildProcessPingThread : public Thread,
|
||||||
pingReceived();
|
pingReceived();
|
||||||
}
|
}
|
||||||
|
|
||||||
void startPinging() { startThread (4); }
|
void startPinging() { startThread (Priority::low); }
|
||||||
|
|
||||||
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
|
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
|
||||||
void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
|
void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ NetworkServiceDiscovery::Advertiser::Advertiser (const String& serviceTypeUID,
|
||||||
message.setAttribute ("address", String());
|
message.setAttribute ("address", String());
|
||||||
message.setAttribute ("port", connectionPort);
|
message.setAttribute ("port", connectionPort);
|
||||||
|
|
||||||
startThread (2);
|
startThread (Priority::background);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkServiceDiscovery::Advertiser::~Advertiser()
|
NetworkServiceDiscovery::Advertiser::~Advertiser()
|
||||||
|
|
@ -92,7 +92,7 @@ NetworkServiceDiscovery::AvailableServiceList::AvailableServiceList (const Strin
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
socket.bindToPort (broadcastPort);
|
socket.bindToPort (broadcastPort);
|
||||||
startThread (2);
|
startThread (Priority::background);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkServiceDiscovery::AvailableServiceList::~AvailableServiceList()
|
NetworkServiceDiscovery::AvailableServiceList::~AvailableServiceList()
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,7 @@ private:
|
||||||
|
|
||||||
void handleAsyncUpdate() override
|
void handleAsyncUpdate() override
|
||||||
{
|
{
|
||||||
startThread (7);
|
startThread (Priority::high);
|
||||||
}
|
}
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ FileBrowserComponent::FileBrowserComponent (int flags_,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The thread must be started before the DirectoryContentsList attempts to scan any file
|
// The thread must be started before the DirectoryContentsList attempts to scan any file
|
||||||
thread.startThread (4);
|
thread.startThread (Thread::Priority::low);
|
||||||
|
|
||||||
fileList.reset (new DirectoryContentsList (this, thread));
|
fileList.reset (new DirectoryContentsList (this, thread));
|
||||||
fileList->setDirectory (currentRoot, true, true);
|
fileList->setDirectory (currentRoot, true, true);
|
||||||
|
|
|
||||||
|
|
@ -1403,7 +1403,7 @@ public:
|
||||||
monitor (mon)
|
monitor (mon)
|
||||||
{
|
{
|
||||||
listeners.push_back (listener);
|
listeners.push_back (listener);
|
||||||
startThread (10);
|
startThread (Priority::highest);
|
||||||
}
|
}
|
||||||
|
|
||||||
~VSyncThread() override
|
~VSyncThread() override
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,11 @@ ThreadWithProgressWindow::~ThreadWithProgressWindow()
|
||||||
stopThread (timeOutMsWhenCancelling);
|
stopThread (timeOutMsWhenCancelling);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadWithProgressWindow::launchThread (int priority)
|
void ThreadWithProgressWindow::launchThread (Priority threadPriority)
|
||||||
{
|
{
|
||||||
JUCE_ASSERT_MESSAGE_THREAD
|
JUCE_ASSERT_MESSAGE_THREAD
|
||||||
|
|
||||||
startThread (priority);
|
startThread (threadPriority);
|
||||||
startTimer (100);
|
startTimer (100);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -105,9 +105,9 @@ void ThreadWithProgressWindow::timerCallback()
|
||||||
void ThreadWithProgressWindow::threadComplete (bool) {}
|
void ThreadWithProgressWindow::threadComplete (bool) {}
|
||||||
|
|
||||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||||
bool ThreadWithProgressWindow::runThread (const int priority)
|
bool ThreadWithProgressWindow::runThread (Priority threadPriority)
|
||||||
{
|
{
|
||||||
launchThread (priority);
|
launchThread (threadPriority);
|
||||||
|
|
||||||
while (isTimerRunning())
|
while (isTimerRunning())
|
||||||
MessageManager::getInstance()->runDispatchLoopUntil (5);
|
MessageManager::getInstance()->runDispatchLoopUntil (5);
|
||||||
|
|
|
||||||
|
|
@ -122,10 +122,10 @@ public:
|
||||||
Before returning, the dialog box will be hidden.
|
Before returning, the dialog box will be hidden.
|
||||||
|
|
||||||
@param priority the priority to use when starting the thread - see
|
@param priority the priority to use when starting the thread - see
|
||||||
Thread::startThread() for values
|
Thread::Priority for values
|
||||||
@returns true if the thread finished normally; false if the user pressed cancel
|
@returns true if the thread finished normally; false if the user pressed cancel
|
||||||
*/
|
*/
|
||||||
bool runThread (int priority = 5);
|
bool runThread (Priority Priority = Priority::normal);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Starts the thread and returns.
|
/** Starts the thread and returns.
|
||||||
|
|
@ -135,9 +135,9 @@ public:
|
||||||
hidden and the threadComplete() method will be called.
|
hidden and the threadComplete() method will be called.
|
||||||
|
|
||||||
@param priority the priority to use when starting the thread - see
|
@param priority the priority to use when starting the thread - see
|
||||||
Thread::startThread() for values
|
Thread::Priority for values
|
||||||
*/
|
*/
|
||||||
void launchThread (int priority = 5);
|
void launchThread (Priority priority = Priority::normal);
|
||||||
|
|
||||||
/** The thread should call this periodically to update the position of the progress bar.
|
/** The thread should call this periodically to update the position of the progress bar.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ struct OnlineUnlockForm::OverlayComp : public Component,
|
||||||
cancelButton->addListener (this);
|
cancelButton->addListener (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
startThread (4);
|
startThread (Priority::normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
~OverlayComp() override
|
~OverlayComp() override
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue