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
|
||||
=====================
|
||||
|
||||
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
|
||||
------
|
||||
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
|
||||
future.
|
||||
|
||||
|
||||
Change
|
||||
------
|
||||
The function AudioIODeviceCallback::audioDeviceIOCallback() was removed.
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ public:
|
|||
// audio setup
|
||||
formatManager.registerBasicFormats();
|
||||
|
||||
thread.startThread (3);
|
||||
thread.startThread (Thread::Priority::normal);
|
||||
|
||||
#ifndef JUCE_DEMO_RUNNER
|
||||
RuntimePermissions::request (RuntimePermissions::recordAudio,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public:
|
|||
{
|
||||
setOpaque (true);
|
||||
imageList.setDirectory (File::getSpecialLocation (File::userPicturesDirectory), true, true);
|
||||
directoryThread.startThread (1);
|
||||
directoryThread.startThread (Thread::Priority::background);
|
||||
|
||||
fileTree.setTitle ("Files");
|
||||
fileTree.addListener (this);
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ public:
|
|||
setOpaque (true);
|
||||
|
||||
movieList.setDirectory (File::getSpecialLocation (File::userMoviesDirectory), true, true);
|
||||
directoryThread.startThread (1);
|
||||
directoryThread.startThread (Thread::Priority::background);
|
||||
|
||||
fileTree.setTitle ("Files");
|
||||
fileTree.addListener (this);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public:
|
|||
SharedTimeSliceThread()
|
||||
: 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),
|
||||
Thread ("JUCE Demo Thread")
|
||||
{
|
||||
// give the threads a random priority, so some will move more
|
||||
// smoothly than others..
|
||||
startThread (Random::getSystemRandom().nextInt (3) + 3);
|
||||
startThread();
|
||||
}
|
||||
|
||||
~DemoThread() override
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ void LatestVersionCheckerAndUpdater::checkForNewVersion (bool background)
|
|||
if (! isThreadRunning())
|
||||
{
|
||||
backgroundCheck = background;
|
||||
startThread (3);
|
||||
startThread (Priority::low);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +373,7 @@ public:
|
|||
: ThreadWithProgressWindow ("Downloading New Version", true, true),
|
||||
asset (a), targetFolder (t), completionCallback (std::move (cb))
|
||||
{
|
||||
launchThread (3);
|
||||
launchThread (Priority::low);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ void MidiOutput::clearAllPendingMessages()
|
|||
|
||||
void MidiOutput::startBackgroundThread()
|
||||
{
|
||||
startThread (9);
|
||||
startThread (Priority::high);
|
||||
}
|
||||
|
||||
void MidiOutput::stopBackgroundThread()
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ public:
|
|||
if (inputDevice != nullptr)
|
||||
env->CallVoidMethod (inputDevice, AudioRecord.startRecording);
|
||||
|
||||
startThread (8);
|
||||
startThread (Priority::high);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -618,7 +618,7 @@ public:
|
|||
if (outputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (outputDevice->handle)))
|
||||
return;
|
||||
|
||||
startThread (9);
|
||||
startThread (Priority::high);
|
||||
|
||||
int count = 1000;
|
||||
|
||||
|
|
|
|||
|
|
@ -1196,7 +1196,7 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels,
|
|||
for (int i = 0; i < inChans.size(); ++i)
|
||||
inChans.getUnchecked(i)->synchronisePosition();
|
||||
|
||||
startThread (9);
|
||||
startThread (Priority::highest);
|
||||
sleep (10);
|
||||
|
||||
notify();
|
||||
|
|
|
|||
|
|
@ -1387,7 +1387,7 @@ public:
|
|||
shouldShutdown = false;
|
||||
deviceSampleRateChanged = false;
|
||||
|
||||
startThread (8);
|
||||
startThread (Priority::high);
|
||||
Thread::sleep (5);
|
||||
|
||||
if (inputDevice != nullptr && inputDevice->client != nullptr)
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ public:
|
|||
void runTest() override
|
||||
{
|
||||
TimeSliceThread timeSlice ("TestBackgroundThread");
|
||||
timeSlice.startThread (5);
|
||||
timeSlice.startThread (Thread::Priority::normal);
|
||||
|
||||
beginTest ("Timeout");
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@
|
|||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
|
|
@ -34,15 +32,15 @@ namespace juce
|
|||
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages);
|
||||
|
||||
/** @internal */
|
||||
class MessageThread
|
||||
class MessageThread : public Thread
|
||||
{
|
||||
public:
|
||||
MessageThread()
|
||||
MessageThread() : Thread ("JUCE Plugin Message Thread")
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
||||
~MessageThread()
|
||||
~MessageThread() override
|
||||
{
|
||||
MessageManager::getInstance()->stopDispatchLoop();
|
||||
stop();
|
||||
|
|
@ -50,51 +48,37 @@ public:
|
|||
|
||||
void start()
|
||||
{
|
||||
if (isRunning())
|
||||
stop();
|
||||
startThread (Priority::high);
|
||||
|
||||
shouldExit = false;
|
||||
|
||||
thread = std::thread { [this]
|
||||
{
|
||||
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();
|
||||
// Wait for setCurrentThreadAsMessageThread() and getInstance to be executed
|
||||
// before leaving this method
|
||||
threadInitialised.wait (10000);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (! isRunning())
|
||||
return;
|
||||
|
||||
shouldExit = true;
|
||||
thread.join();
|
||||
signalThreadShouldExit();
|
||||
stopThread (-1);
|
||||
}
|
||||
|
||||
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:
|
||||
WaitableEvent threadInitialised;
|
||||
std::thread thread;
|
||||
|
||||
std::atomic<bool> shouldExit { false };
|
||||
|
||||
JUCE_DECLARE_NON_MOVEABLE (MessageThread)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageThread)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ AudioThumbnailCache::AudioThumbnailCache (const int maxNumThumbs)
|
|||
maxNumThumbsToStore (maxNumThumbs)
|
||||
{
|
||||
jassert (maxNumThumbsToStore > 0);
|
||||
thread.startThread (2);
|
||||
thread.startThread (Thread::Priority::low);
|
||||
}
|
||||
|
||||
AudioThumbnailCache::~AudioThumbnailCache()
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@
|
|||
#include "zip/juce_ZipFile.cpp"
|
||||
#include "files/juce_FileFilter.cpp"
|
||||
#include "files/juce_WildcardFileFilter.cpp"
|
||||
#include "native/juce_native_ThreadPriorities.h"
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_WINDOWS
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@
|
|||
#include <sys/ptrace.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/time.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
|
||||
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior)
|
||||
#if JUCE_ANDROID && JUCE_MODULE_AVAILABLE_juce_audio_devices && (JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE)
|
||||
#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;
|
||||
int policy, maxp, minp;
|
||||
PosixThreadAttribute attr { threadStackSize };
|
||||
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)
|
||||
policy = SCHED_OTHER;
|
||||
else
|
||||
policy = SCHED_RR;
|
||||
if (androidJNIJavaVM != nullptr)
|
||||
{
|
||||
void* env = nullptr;
|
||||
androidJNIJavaVM->GetEnv (&env, JNI_VERSION_1_2);
|
||||
|
||||
minp = sched_get_priority_min (policy);
|
||||
maxp = sched_get_priority_max (policy);
|
||||
// only detach if we have actually been attached
|
||||
if (env != nullptr)
|
||||
androidJNIJavaVM->DetachCurrentThread();
|
||||
}
|
||||
|
||||
if (p < 2)
|
||||
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);
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
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
|
||||
{
|
||||
StringArray lines;
|
||||
|
|
|
|||
|
|
@ -28,27 +28,49 @@ namespace juce
|
|||
live in juce_posix_SharedCode.h!
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior)
|
||||
bool Thread::createNativeThread (Priority)
|
||||
{
|
||||
auto policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR;
|
||||
auto minp = sched_get_priority_min (policy);
|
||||
auto maxp = sched_get_priority_max (policy);
|
||||
PosixThreadAttribute attr { threadStackSize };
|
||||
PosixSchedulerPriority::getNativeSchedulerAndPriority (realtimeOptions, {}).apply (attr);
|
||||
|
||||
struct sched_param param;
|
||||
|
||||
switch (prior)
|
||||
threadId = threadHandle = makeThreadHandle (attr, this, [] (void* userData) -> void*
|
||||
{
|
||||
case LowPriority:
|
||||
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;
|
||||
}
|
||||
auto* myself = static_cast<Thread*> (userData);
|
||||
|
||||
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()
|
||||
{
|
||||
auto result1 = setreuid (geteuid(), getuid());
|
||||
|
|
|
|||
|
|
@ -29,9 +29,139 @@ namespace juce
|
|||
*/
|
||||
|
||||
#if JUCE_IOS
|
||||
bool isIOSAppActive = true;
|
||||
bool isIOSAppActive = true;
|
||||
#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()
|
||||
{
|
||||
|
|
|
|||
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();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_ANDROID
|
||||
extern JavaVM* androidJNIJavaVM;
|
||||
#endif
|
||||
|
||||
static void* threadEntryProc (void* userData)
|
||||
class PosixThreadAttribute
|
||||
{
|
||||
auto* myself = static_cast<Thread*> (userData);
|
||||
|
||||
JUCE_AUTORELEASEPOOL
|
||||
public:
|
||||
explicit PosixThreadAttribute (size_t stackSize)
|
||||
{
|
||||
juce_threadEntryPoint (myself);
|
||||
if (valid)
|
||||
pthread_attr_setstacksize (&attr, stackSize);
|
||||
}
|
||||
|
||||
#if JUCE_ANDROID
|
||||
if (androidJNIJavaVM != nullptr)
|
||||
~PosixThreadAttribute()
|
||||
{
|
||||
void* env = nullptr;
|
||||
androidJNIJavaVM->GetEnv(&env, JNI_VERSION_1_2);
|
||||
|
||||
// only detach if we have actually been attached
|
||||
if (env != nullptr)
|
||||
androidJNIJavaVM->DetachCurrentThread();
|
||||
if (valid)
|
||||
pthread_attr_destroy (&attr);
|
||||
}
|
||||
#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)
|
||||
#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 = {};
|
||||
private:
|
||||
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;
|
||||
pthread_attr_setstacksize (attrPtr, threadStackSize);
|
||||
int scheduler{};
|
||||
sched_param param{};
|
||||
pthread_getschedparam (pthread_self(), &scheduler, ¶m);
|
||||
return { scheduler, param.sched_priority };
|
||||
}
|
||||
|
||||
|
||||
if (pthread_create (&handle, attrPtr, threadEntryProc, this) == 0)
|
||||
static PosixSchedulerPriority getNativeSchedulerAndPriority (const Optional<Thread::RealtimeOptions>& rt,
|
||||
[[maybe_unused]] Thread::Priority prio)
|
||||
{
|
||||
pthread_detach (handle);
|
||||
threadHandle = (void*) handle;
|
||||
threadId = (ThreadID) threadHandle.get();
|
||||
const auto isRealtime = rt.hasValue();
|
||||
|
||||
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)
|
||||
pthread_attr_destroy (attrPtr);
|
||||
void apply ([[maybe_unused]] PosixThreadAttribute& attr) const
|
||||
{
|
||||
#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()
|
||||
{
|
||||
threadId = {};
|
||||
threadHandle = {};
|
||||
}
|
||||
|
||||
void Thread::killThread()
|
||||
{
|
||||
if (threadHandle.get() != nullptr)
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
jassertfalse; // pthread_cancel not available!
|
||||
#else
|
||||
pthread_cancel ((pthread_t) threadHandle.get());
|
||||
#endif
|
||||
}
|
||||
threadHandle = nullptr;
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
|
||||
|
|
@ -963,41 +996,6 @@ void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
|
|||
#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()
|
||||
{
|
||||
return (ThreadID) pthread_self();
|
||||
|
|
@ -1262,145 +1260,4 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
|
|||
|
||||
#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
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRI
|
|||
bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) &lock) != FALSE; }
|
||||
void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) &lock); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
static unsigned int STDMETHODCALLTYPE threadEntryProc (void* userData)
|
||||
{
|
||||
|
|
@ -65,31 +64,72 @@ static unsigned int STDMETHODCALLTYPE threadEntryProc (void* userData)
|
|||
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;
|
||||
threadHandle = (void*) _beginthreadex (nullptr, (unsigned int) threadStackSize,
|
||||
&threadEntryProc, this, 0, &newThreadId);
|
||||
threadId = (ThreadID) (pointer_sized_int) newThreadId;
|
||||
&threadEntryProc, this, CREATE_SUSPENDED,
|
||||
&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()
|
||||
{
|
||||
CloseHandle ((HANDLE) threadHandle.get());
|
||||
CloseHandle (threadHandle);
|
||||
threadId = nullptr;
|
||||
threadHandle = nullptr;
|
||||
}
|
||||
|
||||
void Thread::killThread()
|
||||
{
|
||||
if (threadHandle.get() != nullptr)
|
||||
if (threadHandle != nullptr)
|
||||
{
|
||||
#if JUCE_DEBUG
|
||||
OutputDebugStringA ("** Warning - Forced thread termination **\n");
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6258)
|
||||
TerminateThread (threadHandle.get(), 0);
|
||||
TerminateThread (threadHandle, 0);
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
}
|
||||
|
|
@ -129,23 +169,6 @@ Thread::ThreadID JUCE_CALLTYPE Thread::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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -527,56 +550,4 @@ bool ChildProcess::start (const StringArray& args, int 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
|
||||
|
|
|
|||
|
|
@ -243,7 +243,6 @@ public:
|
|||
*/
|
||||
static bool isRunningInAppExtensionSandbox() noexcept;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("This method was spelt wrong! Please change your code to use getCpuSpeedInMegahertz instead.")]]
|
||||
|
|
|
|||
|
|
@ -23,13 +23,96 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
HighResolutionTimer::HighResolutionTimer() : pimpl (new Pimpl (*this)) {}
|
||||
HighResolutionTimer::~HighResolutionTimer() { stopTimer(); }
|
||||
class HighResolutionTimer::Pimpl : private Thread
|
||||
{
|
||||
using steady_clock = std::chrono::steady_clock;
|
||||
using milliseconds = std::chrono::milliseconds;
|
||||
|
||||
void HighResolutionTimer::startTimer (int periodMs) { pimpl->start (jmax (1, periodMs)); }
|
||||
void HighResolutionTimer::stopTimer() { pimpl->stop(); }
|
||||
public:
|
||||
explicit Pimpl (HighResolutionTimer& ownerRef)
|
||||
: Thread ("HighResolutionTimerThread"),
|
||||
owner (ownerRef)
|
||||
{
|
||||
}
|
||||
|
||||
bool HighResolutionTimer::isTimerRunning() const noexcept { return pimpl->periodMs != 0; }
|
||||
int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->periodMs; }
|
||||
using Thread::isThreadRunning;
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ public:
|
|||
int getTimerInterval() const noexcept;
|
||||
|
||||
private:
|
||||
struct Pimpl;
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public:
|
|||
@param priority the process priority, where
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@
|
|||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
Thread::Thread (const String& name, size_t stackSize)
|
||||
: threadName (name), threadStackSize (stackSize)
|
||||
//==============================================================================
|
||||
Thread::Thread (const String& name, size_t stackSize) : threadName (name),
|
||||
threadStackSize (stackSize)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -81,12 +81,18 @@ void Thread::threadEntryPoint()
|
|||
const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
|
||||
currentThreadHolder->value = this;
|
||||
|
||||
#if JUCE_ANDROID
|
||||
setPriority (priority);
|
||||
#endif
|
||||
|
||||
if (threadName.isNotEmpty())
|
||||
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))
|
||||
{
|
||||
jassert (getCurrentThreadId() == threadId.get());
|
||||
jassert (getCurrentThreadId() == threadId);
|
||||
|
||||
if (affinityMask != 0)
|
||||
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();
|
||||
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);
|
||||
|
||||
if (threadHandle.get() == nullptr)
|
||||
if (threadHandle == nullptr)
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
isAndroidRealtimeThread = (priority == realtimeAudioPriority);
|
||||
#endif
|
||||
realtimeOptions.reset();
|
||||
return startThreadInternal (threadPriority);
|
||||
}
|
||||
|
||||
threadPriority = getAdjustedPriority (priority);
|
||||
startThread();
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return threadHandle.get() != nullptr;
|
||||
return threadHandle != nullptr;
|
||||
}
|
||||
|
||||
Thread* JUCE_CALLTYPE Thread::getCurrentThread()
|
||||
|
|
@ -164,19 +193,19 @@ Thread* JUCE_CALLTYPE Thread::getCurrentThread()
|
|||
|
||||
Thread::ThreadID Thread::getThreadId() const noexcept
|
||||
{
|
||||
return threadId.get();
|
||||
return threadId;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Thread::signalThreadShouldExit()
|
||||
{
|
||||
shouldExit = 1;
|
||||
shouldExit = true;
|
||||
listeners.call ([] (Listener& l) { l.exitSignalSent(); });
|
||||
}
|
||||
|
||||
bool Thread::threadShouldExit() const
|
||||
{
|
||||
return shouldExit.get() != 0;
|
||||
return shouldExit;
|
||||
}
|
||||
|
||||
bool Thread::currentThreadShouldExit()
|
||||
|
|
@ -249,40 +278,9 @@ void Thread::removeListener (Listener* listener)
|
|||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Thread::setPriority (int newPriority)
|
||||
bool Thread::isRealtime() const
|
||||
{
|
||||
newPriority = getAdjustedPriority (newPriority);
|
||||
|
||||
// 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));
|
||||
return realtimeOptions.hasValue();
|
||||
}
|
||||
|
||||
void Thread::setAffinityMask (const uint32 newAffinityMask)
|
||||
|
|
@ -290,11 +288,6 @@ void Thread::setAffinityMask (const uint32 newAffinityMask)
|
|||
affinityMask = newAffinityMask;
|
||||
}
|
||||
|
||||
int Thread::getAdjustedPriority (int newPriority)
|
||||
{
|
||||
return jlimit (0, 10, newPriority == realtimeAudioPriority ? 9 : newPriority);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Thread::wait (const int timeOutMilliseconds) const
|
||||
{
|
||||
|
|
@ -349,7 +342,6 @@ bool JUCE_CALLTYPE Process::isRunningUnderDebugger() noexcept
|
|||
return juce_isRunningUnderDebugger();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ namespace juce
|
|||
Encapsulates a thread.
|
||||
|
||||
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
|
||||
and controlled with various other methods.
|
||||
do their business. The thread can then be started with the startThread() or
|
||||
startRealtimeThread() methods and controlled with various other methods.
|
||||
|
||||
This class also contains some thread-related static methods, such
|
||||
as sleep(), yield(), getCurrentThreadId() etc.
|
||||
|
|
@ -42,6 +42,46 @@ namespace juce
|
|||
class JUCE_API Thread
|
||||
{
|
||||
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.
|
||||
|
|
@ -78,23 +118,51 @@ public:
|
|||
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.
|
||||
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
|
||||
*/
|
||||
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.
|
||||
If the thread is already running, its priority will be changed.
|
||||
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.
|
||||
|
||||
@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.
|
||||
|
||||
|
|
@ -198,50 +266,8 @@ public:
|
|||
/** Removes a listener added with addListener. */
|
||||
void removeListener (Listener*);
|
||||
|
||||
//==============================================================================
|
||||
/** Special realtime audio thread priority
|
||||
|
||||
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);
|
||||
/** Returns true if this Thread represents a realtime thread. */
|
||||
bool isRealtime() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the affinity mask for the thread.
|
||||
|
|
@ -380,34 +406,58 @@ public:
|
|||
static void initialiseJUCE (void* jniEnv, void* jContext);
|
||||
#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:
|
||||
//==============================================================================
|
||||
const String threadName;
|
||||
Atomic<void*> threadHandle { nullptr };
|
||||
Atomic<ThreadID> threadId = {};
|
||||
std::atomic<void*> threadHandle { nullptr };
|
||||
std::atomic<ThreadID> threadId { nullptr };
|
||||
Optional<RealtimeOptions> realtimeOptions = {};
|
||||
CriticalSection startStopLock;
|
||||
WaitableEvent startSuspensionEvent, defaultEvent;
|
||||
int threadPriority = 5;
|
||||
size_t threadStackSize;
|
||||
uint32 affinityMask = 0;
|
||||
bool deleteOnThreadEnd = false;
|
||||
Atomic<int32> shouldExit { 0 };
|
||||
std::atomic<bool> shouldExit { false };
|
||||
ListenerList<Listener, Array<Listener*, CriticalSection>> listeners;
|
||||
|
||||
#if JUCE_ANDROID
|
||||
bool isAndroidRealtimeThread = false;
|
||||
#if JUCE_ANDROID || JUCE_LINUX || JUCE_BSD
|
||||
std::atomic<Priority> priority;
|
||||
#endif
|
||||
|
||||
#ifndef DOXYGEN
|
||||
friend void JUCE_API juce_threadEntryPoint (void*);
|
||||
#endif
|
||||
|
||||
void launchThread();
|
||||
bool startThreadInternal (Priority);
|
||||
bool createNativeThread (Priority);
|
||||
void closeThreadHandle();
|
||||
void killThread();
|
||||
void threadEntryPoint();
|
||||
static bool setThreadPriority (void*, int);
|
||||
static int getAdjustedPriority (int);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,11 +33,14 @@ struct ThreadPool::ThreadPoolThread : public Thread
|
|||
void run() override
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (! pool.runNextJob (*this))
|
||||
wait (500);
|
||||
}
|
||||
}
|
||||
|
||||
std::atomic<ThreadPoolJob*> currentJob { nullptr };
|
||||
|
||||
ThreadPool& pool;
|
||||
|
||||
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!
|
||||
|
||||
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()
|
||||
|
|
@ -108,15 +114,6 @@ ThreadPool::~ThreadPool()
|
|||
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()
|
||||
{
|
||||
for (auto* t : threads)
|
||||
|
|
@ -330,17 +327,6 @@ StringArray ThreadPool::getNamesOfAllJobs (bool onlyReturnActiveJobs) const
|
|||
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()
|
||||
{
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
|
|
|||
|
|
@ -164,8 +164,9 @@ public:
|
|||
@param threadStackSize the size of the stack of each thread. If this value
|
||||
is zero then the default stack size of the OS will
|
||||
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.
|
||||
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;
|
||||
|
||||
/** 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:
|
||||
//==============================================================================
|
||||
Array<ThreadPoolJob*> jobs;
|
||||
|
|
@ -330,7 +324,6 @@ private:
|
|||
bool runNextJob (ThreadPoolThread&);
|
||||
ThreadPoolJob* pickNextJobToRun();
|
||||
void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
|
||||
void createThreads (int numThreads, size_t threadStackSize = 0);
|
||||
void stopThreads();
|
||||
|
||||
// Note that this method has changed, and no longer has a parameter to indicate
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ struct ChildProcessPingThread : public Thread,
|
|||
pingReceived();
|
||||
}
|
||||
|
||||
void startPinging() { startThread (4); }
|
||||
void startPinging() { startThread (Priority::low); }
|
||||
|
||||
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
|
||||
void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ NetworkServiceDiscovery::Advertiser::Advertiser (const String& serviceTypeUID,
|
|||
message.setAttribute ("address", String());
|
||||
message.setAttribute ("port", connectionPort);
|
||||
|
||||
startThread (2);
|
||||
startThread (Priority::background);
|
||||
}
|
||||
|
||||
NetworkServiceDiscovery::Advertiser::~Advertiser()
|
||||
|
|
@ -92,7 +92,7 @@ NetworkServiceDiscovery::AvailableServiceList::AvailableServiceList (const Strin
|
|||
#endif
|
||||
|
||||
socket.bindToPort (broadcastPort);
|
||||
startThread (2);
|
||||
startThread (Priority::background);
|
||||
}
|
||||
|
||||
NetworkServiceDiscovery::AvailableServiceList::~AvailableServiceList()
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ private:
|
|||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
startThread (7);
|
||||
startThread (Priority::high);
|
||||
}
|
||||
|
||||
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
|
||||
thread.startThread (4);
|
||||
thread.startThread (Thread::Priority::low);
|
||||
|
||||
fileList.reset (new DirectoryContentsList (this, thread));
|
||||
fileList->setDirectory (currentRoot, true, true);
|
||||
|
|
|
|||
|
|
@ -1403,7 +1403,7 @@ public:
|
|||
monitor (mon)
|
||||
{
|
||||
listeners.push_back (listener);
|
||||
startThread (10);
|
||||
startThread (Priority::highest);
|
||||
}
|
||||
|
||||
~VSyncThread() override
|
||||
|
|
|
|||
|
|
@ -56,11 +56,11 @@ ThreadWithProgressWindow::~ThreadWithProgressWindow()
|
|||
stopThread (timeOutMsWhenCancelling);
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::launchThread (int priority)
|
||||
void ThreadWithProgressWindow::launchThread (Priority threadPriority)
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
|
||||
startThread (priority);
|
||||
startThread (threadPriority);
|
||||
startTimer (100);
|
||||
|
||||
{
|
||||
|
|
@ -105,9 +105,9 @@ void ThreadWithProgressWindow::timerCallback()
|
|||
void ThreadWithProgressWindow::threadComplete (bool) {}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool ThreadWithProgressWindow::runThread (const int priority)
|
||||
bool ThreadWithProgressWindow::runThread (Priority threadPriority)
|
||||
{
|
||||
launchThread (priority);
|
||||
launchThread (threadPriority);
|
||||
|
||||
while (isTimerRunning())
|
||||
MessageManager::getInstance()->runDispatchLoopUntil (5);
|
||||
|
|
|
|||
|
|
@ -122,10 +122,10 @@ public:
|
|||
Before returning, the dialog box will be hidden.
|
||||
|
||||
@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
|
||||
*/
|
||||
bool runThread (int priority = 5);
|
||||
bool runThread (Priority Priority = Priority::normal);
|
||||
#endif
|
||||
|
||||
/** Starts the thread and returns.
|
||||
|
|
@ -135,9 +135,9 @@ public:
|
|||
hidden and the threadComplete() method will be called.
|
||||
|
||||
@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.
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ struct OnlineUnlockForm::OverlayComp : public Component,
|
|||
cancelButton->addListener (this);
|
||||
}
|
||||
|
||||
startThread (4);
|
||||
startThread (Priority::normal);
|
||||
}
|
||||
|
||||
~OverlayComp() override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue