mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
HiResTimer: Tidy up POSIX implementation using std threading constructs
This commit is contained in:
parent
6787230dd4
commit
857f665f74
2 changed files with 72 additions and 155 deletions
|
|
@ -36,9 +36,10 @@
|
|||
|
||||
#include "juce_core.h"
|
||||
|
||||
#include <locale>
|
||||
#include <cctype>
|
||||
#include <cstdarg>
|
||||
#include <locale>
|
||||
#include <thread>
|
||||
|
||||
#if ! JUCE_ANDROID
|
||||
#include <sys/timeb.h>
|
||||
|
|
|
|||
|
|
@ -1224,19 +1224,9 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
|
|||
//==============================================================================
|
||||
struct HighResolutionTimer::Pimpl
|
||||
{
|
||||
Pimpl (HighResolutionTimer& t) : owner (t)
|
||||
{
|
||||
pthread_condattr_t attr;
|
||||
pthread_condattr_init (&attr);
|
||||
|
||||
#if JUCE_LINUX || (JUCE_ANDROID && defined(__ANDROID_API__) && __ANDROID_API__ >= 21)
|
||||
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
|
||||
#endif
|
||||
|
||||
pthread_cond_init (&stopCond, &attr);
|
||||
pthread_condattr_destroy (&attr);
|
||||
pthread_mutex_init (&timerMutex, nullptr);
|
||||
}
|
||||
explicit Pimpl (HighResolutionTimer& t)
|
||||
: owner (t)
|
||||
{}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
|
|
@ -1246,176 +1236,103 @@ struct HighResolutionTimer::Pimpl
|
|||
|
||||
void start (int newPeriod)
|
||||
{
|
||||
if (periodMs != newPeriod)
|
||||
if (periodMs == newPeriod)
|
||||
return;
|
||||
|
||||
if (thread.get_id() == std::this_thread::get_id())
|
||||
{
|
||||
if (thread != pthread_self())
|
||||
{
|
||||
stop();
|
||||
|
||||
periodMs = newPeriod;
|
||||
|
||||
if (pthread_create (&thread, nullptr, timerThread, this) == 0)
|
||||
setThreadToRealtime (thread, (uint64) newPeriod);
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
else
|
||||
{
|
||||
periodMs = newPeriod;
|
||||
}
|
||||
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;
|
||||
|
||||
if (thread == pthread_t() || thread == pthread_self())
|
||||
const auto thread_id = thread.get_id();
|
||||
|
||||
if (thread_id == std::thread::id() || thread_id == std::this_thread::get_id())
|
||||
return;
|
||||
|
||||
pthread_mutex_lock (&timerMutex);
|
||||
pthread_cond_signal (&stopCond);
|
||||
pthread_mutex_unlock (&timerMutex);
|
||||
{
|
||||
std::unique_lock<std::mutex> unique_lock (timerMutex);
|
||||
stopCond.notify_one();
|
||||
}
|
||||
|
||||
pthread_join (thread, nullptr);
|
||||
thread = {};
|
||||
thread.join();
|
||||
}
|
||||
|
||||
HighResolutionTimer& owner;
|
||||
std::atomic<int> periodMs { 0 };
|
||||
|
||||
private:
|
||||
pthread_t thread = {};
|
||||
pthread_cond_t stopCond;
|
||||
pthread_mutex_t timerMutex;
|
||||
std::thread thread;
|
||||
std::condition_variable stopCond;
|
||||
std::mutex timerMutex;
|
||||
|
||||
static void* timerThread (void* param)
|
||||
class Clock
|
||||
{
|
||||
#if ! JUCE_ANDROID
|
||||
int dummy;
|
||||
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy);
|
||||
#endif
|
||||
public:
|
||||
explicit Clock (std::chrono::steady_clock::rep millis) noexcept
|
||||
: time (std::chrono::steady_clock::now()),
|
||||
delta (std::chrono::milliseconds (millis))
|
||||
{}
|
||||
|
||||
reinterpret_cast<Pimpl*> (param)->timerThread();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void timerThread()
|
||||
{
|
||||
auto lastPeriod = periodMs.load();
|
||||
Clock clock (lastPeriod);
|
||||
|
||||
pthread_mutex_lock (&timerMutex);
|
||||
|
||||
while (periodMs != 0)
|
||||
bool wait (std::condition_variable& cond, std::unique_lock<std::mutex>& lock) noexcept
|
||||
{
|
||||
clock.next();
|
||||
while (periodMs != 0 && clock.wait (stopCond, timerMutex));
|
||||
|
||||
if (periodMs == 0)
|
||||
break;
|
||||
|
||||
owner.hiResTimerCallback();
|
||||
|
||||
auto newPeriod = periodMs.load();
|
||||
|
||||
if (lastPeriod != newPeriod)
|
||||
{
|
||||
lastPeriod = newPeriod;
|
||||
clock = Clock (lastPeriod);
|
||||
}
|
||||
return cond.wait_until (lock, time) != std::cv_status::timeout;
|
||||
}
|
||||
|
||||
periodMs = 0;
|
||||
pthread_mutex_unlock (&timerMutex);
|
||||
pthread_exit (nullptr);
|
||||
}
|
||||
|
||||
struct Clock
|
||||
{
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
Clock (double millis) noexcept
|
||||
{
|
||||
(void) mach_timebase_info (&timebase);
|
||||
delta = (((uint64_t) (millis * 1000000.0)) * timebase.denom) / timebase.numer;
|
||||
time = mach_absolute_time();
|
||||
}
|
||||
|
||||
bool wait (pthread_cond_t& cond, pthread_mutex_t& mutex) noexcept
|
||||
{
|
||||
struct timespec left;
|
||||
|
||||
if (! hasExpired (left))
|
||||
return (pthread_cond_timedwait_relative_np (&cond, &mutex, &left) != ETIMEDOUT);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t time, delta;
|
||||
mach_timebase_info_data_t timebase;
|
||||
|
||||
bool hasExpired (struct timespec& time_left) noexcept
|
||||
{
|
||||
uint64_t now = mach_absolute_time();
|
||||
|
||||
if (now < time)
|
||||
{
|
||||
uint64_t left = time - now;
|
||||
uint64_t nanos = (left * static_cast<uint64_t> (timebase.numer)) / static_cast<uint64_t> (timebase.denom);
|
||||
time_left.tv_sec = static_cast<__darwin_time_t> (nanos / 1000000000ULL);
|
||||
time_left.tv_nsec = static_cast<long> (nanos - (static_cast<uint64_t> (time_left.tv_sec) * 1000000000ULL));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
Clock (double millis) noexcept : delta ((uint64) (millis * 1000000))
|
||||
{
|
||||
struct timespec t;
|
||||
clock_gettime (CLOCK_MONOTONIC, &t);
|
||||
time = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec);
|
||||
}
|
||||
|
||||
bool wait (pthread_cond_t& cond, pthread_mutex_t& mutex) noexcept
|
||||
{
|
||||
struct timespec absExpire;
|
||||
|
||||
if (! hasExpired (absExpire))
|
||||
return (pthread_cond_timedwait (&cond, &mutex, &absExpire) != ETIMEDOUT);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64 time, delta;
|
||||
|
||||
bool hasExpired (struct timespec& expiryTime) noexcept
|
||||
{
|
||||
struct timespec t;
|
||||
clock_gettime (CLOCK_MONOTONIC, &t);
|
||||
auto now = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec);
|
||||
|
||||
if (now < time)
|
||||
{
|
||||
expiryTime.tv_sec = (time_t) (time / 1000000000);
|
||||
expiryTime.tv_nsec = (long) (time % 1000000000);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void next() noexcept
|
||||
{
|
||||
time += delta;
|
||||
}
|
||||
|
||||
private:
|
||||
std::chrono::time_point<std::chrono::steady_clock> time;
|
||||
std::chrono::steady_clock::duration delta;
|
||||
};
|
||||
|
||||
static bool setThreadToRealtime (pthread_t thread, uint64 periodMs)
|
||||
static bool setThisThreadToRealtime (uint64 periodMs)
|
||||
{
|
||||
const auto thread = pthread_self();
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
thread_time_constraint_policy_data_t policy;
|
||||
policy.period = (uint32_t) (periodMs * 1000000);
|
||||
|
|
@ -1433,7 +1350,6 @@ private:
|
|||
struct sched_param param;
|
||||
param.sched_priority = sched_get_priority_max (SCHED_RR);
|
||||
return pthread_setschedparam (thread, SCHED_RR, ¶m) == 0;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue