| @@ -1215,13 +1215,24 @@ bool ChildProcess::start (const StringArray& args, int streamFlags) | |||||
| #if ! JUCE_ANDROID | #if ! JUCE_ANDROID | ||||
| struct HighResolutionTimer::Pimpl | struct HighResolutionTimer::Pimpl | ||||
| { | { | ||||
| Pimpl (HighResolutionTimer& t) : owner (t), thread (0), shouldStop (false) | |||||
| Pimpl (HighResolutionTimer& t) : owner (t), thread (0), destroyThread (false), isRunning (false) | |||||
| { | { | ||||
| pthread_condattr_t attr; | |||||
| pthread_condattr_init (&attr); | |||||
| #if ! (JUCE_MAC || JUCE_IOS) | |||||
| pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); | |||||
| #endif | |||||
| pthread_cond_init (&stopCond, &attr); | |||||
| pthread_condattr_destroy (&attr); | |||||
| pthread_mutex_init (&timerMutex, nullptr); | |||||
| } | } | ||||
| ~Pimpl() | ~Pimpl() | ||||
| { | { | ||||
| jassert (thread == 0); | |||||
| jassert (! isRunning); | |||||
| stop(); | |||||
| } | } | ||||
| void start (int newPeriod) | void start (int newPeriod) | ||||
| @@ -1233,7 +1244,8 @@ struct HighResolutionTimer::Pimpl | |||||
| stop(); | stop(); | ||||
| periodMs = newPeriod; | periodMs = newPeriod; | ||||
| shouldStop = false; | |||||
| destroyThread = false; | |||||
| isRunning = true; | |||||
| if (pthread_create (&thread, nullptr, timerThread, this) == 0) | if (pthread_create (&thread, nullptr, timerThread, this) == 0) | ||||
| setThreadToRealtime (thread, (uint64) newPeriod); | setThreadToRealtime (thread, (uint64) newPeriod); | ||||
| @@ -1243,25 +1255,34 @@ struct HighResolutionTimer::Pimpl | |||||
| else | else | ||||
| { | { | ||||
| periodMs = newPeriod; | periodMs = newPeriod; | ||||
| shouldStop = false; | |||||
| isRunning = true; | |||||
| destroyThread = false; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void stop() | void stop() | ||||
| { | { | ||||
| if (thread != 0) | |||||
| { | |||||
| shouldStop = true; | |||||
| isRunning = false; | |||||
| while (thread != 0 && thread != pthread_self()) | |||||
| { | |||||
| // if the timer callback itself calls startTimer then we need | |||||
| // to override this | |||||
| shouldStop = true; | |||||
| Thread::yield(); | |||||
| } | |||||
| if (thread == 0) | |||||
| return; | |||||
| if (thread == pthread_self()) | |||||
| { | |||||
| periodMs = 3600000; | |||||
| return; | |||||
| } | } | ||||
| isRunning = false; | |||||
| destroyThread = true; | |||||
| pthread_mutex_lock (&timerMutex); | |||||
| pthread_cond_signal (&stopCond); | |||||
| pthread_mutex_unlock (&timerMutex); | |||||
| pthread_join (thread, nullptr); | |||||
| thread = 0; | |||||
| } | } | ||||
| HighResolutionTimer& owner; | HighResolutionTimer& owner; | ||||
| @@ -1269,7 +1290,11 @@ struct HighResolutionTimer::Pimpl | |||||
| private: | private: | ||||
| pthread_t thread; | pthread_t thread; | ||||
| bool volatile shouldStop; | |||||
| pthread_cond_t stopCond; | |||||
| pthread_mutex_t timerMutex; | |||||
| bool volatile destroyThread; | |||||
| bool volatile isRunning; | |||||
| static void* timerThread (void* param) | static void* timerThread (void* param) | ||||
| { | { | ||||
| @@ -1289,14 +1314,18 @@ private: | |||||
| int lastPeriod = periodMs; | int lastPeriod = periodMs; | ||||
| Clock clock (lastPeriod); | Clock clock (lastPeriod); | ||||
| while (! shouldStop) | |||||
| pthread_mutex_lock (&timerMutex); | |||||
| while (! destroyThread) | |||||
| { | { | ||||
| clock.wait(); | |||||
| clock.next(); | |||||
| while (! destroyThread && clock.wait (stopCond, timerMutex)); | |||||
| if (shouldStop) | |||||
| if (destroyThread) | |||||
| break; | break; | ||||
| owner.hiResTimerCallback(); | |||||
| if (isRunning) | |||||
| owner.hiResTimerCallback(); | |||||
| if (lastPeriod != periodMs) | if (lastPeriod != periodMs) | ||||
| { | { | ||||
| @@ -1306,7 +1335,9 @@ private: | |||||
| } | } | ||||
| periodMs = 0; | periodMs = 0; | ||||
| thread = 0; | |||||
| pthread_mutex_unlock (&timerMutex); | |||||
| pthread_exit (nullptr); | |||||
| } | } | ||||
| struct Clock | struct Clock | ||||
| @@ -1314,21 +1345,41 @@ private: | |||||
| #if JUCE_MAC || JUCE_IOS | #if JUCE_MAC || JUCE_IOS | ||||
| Clock (double millis) noexcept | Clock (double millis) noexcept | ||||
| { | { | ||||
| mach_timebase_info_data_t timebase; | |||||
| (void) mach_timebase_info (&timebase); | (void) mach_timebase_info (&timebase); | ||||
| delta = (((uint64_t) (millis * 1000000.0)) * timebase.denom) / timebase.numer; | delta = (((uint64_t) (millis * 1000000.0)) * timebase.denom) / timebase.numer; | ||||
| time = mach_absolute_time(); | time = mach_absolute_time(); | ||||
| } | } | ||||
| void wait() noexcept | |||||
| bool wait (pthread_cond_t& cond, pthread_mutex_t& mutex) noexcept | |||||
| { | { | ||||
| time += delta; | |||||
| mach_wait_until (time); | |||||
| struct timespec left; | |||||
| if (! hasExpired (left)) | |||||
| return (pthread_cond_timedwait_relative_np (&cond, &mutex, &left) != ETIMEDOUT); | |||||
| return false; | |||||
| } | } | ||||
| uint64_t time, delta; | uint64_t time, delta; | ||||
| mach_timebase_info_data_t timebase; | |||||
| #else | |||||
| 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)) | Clock (double millis) noexcept : delta ((uint64) (millis * 1000000)) | ||||
| { | { | ||||
| struct timespec t; | struct timespec t; | ||||
| @@ -1336,19 +1387,40 @@ private: | |||||
| time = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec); | time = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec); | ||||
| } | } | ||||
| void wait() noexcept | |||||
| bool wait (pthread_cond_t& cond, pthread_mutex_t& mutex) noexcept | |||||
| { | { | ||||
| time += delta; | |||||
| struct timespec absExpire; | |||||
| struct timespec t; | |||||
| t.tv_sec = (time_t) (time / 1000000000); | |||||
| t.tv_nsec = (long) (time % 1000000000); | |||||
| if (! hasExpired (absExpire)) | |||||
| return (pthread_cond_timedwait (&cond, &mutex, &absExpire) != ETIMEDOUT); | |||||
| clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &t, nullptr); | |||||
| return false; | |||||
| } | } | ||||
| uint64 time, delta; | uint64 time, delta; | ||||
| bool hasExpired(struct timespec& expiryTime) noexcept | |||||
| { | |||||
| struct timespec t; | |||||
| clock_gettime (CLOCK_MONOTONIC, &t); | |||||
| uint64 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 | #endif | ||||
| void next() noexcept | |||||
| { | |||||
| time += delta; | |||||
| } | |||||
| }; | }; | ||||
| static bool setThreadToRealtime (pthread_t thread, uint64 periodMs) | static bool setThreadToRealtime (pthread_t thread, uint64 periodMs) | ||||