Browse Source

Fixed a bug where the high resolution timer could hang when stopped from another thread

tags/2021-05-28
hogliux 9 years ago
parent
commit
1c4b687401
1 changed files with 103 additions and 31 deletions
  1. +103
    -31
      modules/juce_core/native/juce_posix_SharedCode.h

+ 103
- 31
modules/juce_core/native/juce_posix_SharedCode.h View File

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


Loading…
Cancel
Save