From 857f665f744b3afd25d47ceb095393b052e825d6 Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 17 Dec 2020 15:42:04 +0000 Subject: [PATCH] HiResTimer: Tidy up POSIX implementation using std threading constructs --- modules/juce_core/juce_core.cpp | 3 +- .../juce_core/native/juce_posix_SharedCode.h | 224 ++++++------------ 2 files changed, 72 insertions(+), 155 deletions(-) diff --git a/modules/juce_core/juce_core.cpp b/modules/juce_core/juce_core.cpp index 914cae6806..0c9b604f24 100644 --- a/modules/juce_core/juce_core.cpp +++ b/modules/juce_core/juce_core.cpp @@ -36,9 +36,10 @@ #include "juce_core.h" -#include #include #include +#include +#include #if ! JUCE_ANDROID #include diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h index 7efeb5e642..2891d0160f 100644 --- a/modules/juce_core/native/juce_posix_SharedCode.h +++ b/modules/juce_core/native/juce_posix_SharedCode.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 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 unique_lock (timerMutex); + stopCond.notify_one(); + } - pthread_join (thread, nullptr); - thread = {}; + thread.join(); } HighResolutionTimer& owner; std::atomic 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 (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& 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 (timebase.numer)) / static_cast (timebase.denom); - time_left.tv_sec = static_cast<__darwin_time_t> (nanos / 1000000000ULL); - time_left.tv_nsec = static_cast (nanos - (static_cast (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 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 }