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
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()
{
jassert (thread == 0);
jassert (! isRunning);
stop();
}
void start (int newPeriod)
@@ -1233,7 +1244,8 @@ struct HighResolutionTimer::Pimpl
stop();
periodMs = newPeriod;
shouldStop = false;
destroyThread = false;
isRunning = true;
if (pthread_create (&thread, nullptr, timerThread, this) == 0)
setThreadToRealtime (thread, (uint64) newPeriod);
@@ -1243,25 +1255,34 @@ struct HighResolutionTimer::Pimpl
else
{
periodMs = newPeriod;
shouldStop = false;
isRunning = true;
destroyThread = false;
}
}
}
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;
@@ -1269,7 +1290,11 @@ struct HighResolutionTimer::Pimpl
private:
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)
{
@@ -1289,14 +1314,18 @@ private:
int lastPeriod = periodMs;
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;
owner.hiResTimerCallback();
if (isRunning)
owner.hiResTimerCallback();
if (lastPeriod != periodMs)
{
@@ -1306,7 +1335,9 @@ private:
}
periodMs = 0;
thread = 0;
pthread_mutex_unlock (&timerMutex);
pthread_exit (nullptr);
}
struct Clock
@@ -1314,21 +1345,41 @@ private:
#if JUCE_MAC || JUCE_IOS
Clock (double millis) noexcept
{
mach_timebase_info_data_t timebase;
(void) mach_timebase_info (&timebase);
delta = (((uint64_t) (millis * 1000000.0)) * timebase.denom) / timebase.numer;
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;
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))
{
struct timespec t;
@@ -1336,19 +1387,40 @@ private:
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;
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
void next() noexcept
{
time += delta;
}
};
static bool setThreadToRealtime (pthread_t thread, uint64 periodMs)


Loading…
Cancel
Save