1
0
Fork 0
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:
reuk 2020-12-17 15:42:04 +00:00
parent 6787230dd4
commit 857f665f74
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11
2 changed files with 72 additions and 155 deletions

View file

@ -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>

View file

@ -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, &param) == 0;
#endif
}