1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-02-01 03:10:06 +00:00

Timer: Ensure the timer thread is deleted before any static or global destructors run

In cases where there was a static or global instance of a class that
inherited from Timer, due to the order of events when a dll is unloaded
on windows, there was a risk of an infinite hang. Deleting the timer
thread before the dll is unloaded avoids this occurring.
This commit is contained in:
Anthony Nicholls 2024-05-08 13:38:45 +01:00
parent 5b98bfdc0f
commit bbc63ba151
2 changed files with 26 additions and 18 deletions

View file

@ -23,21 +23,20 @@
namespace juce
{
class Timer::TimerThread final : private Thread
class Timer::TimerThread final : private Thread,
private DeletedAtShutdown
{
public:
using LockType = CriticalSection; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
using LockType = CriticalSection;
TimerThread() : Thread ("JUCE Timer")
{
timers.reserve (32);
}
JUCE_DECLARE_SINGLETON (TimerThread, true)
~TimerThread() override
{
signalThreadShouldExit();
callbackArrived.signal();
stopThread (-1);
clearSingletonInstance();
}
void run() override
@ -204,8 +203,8 @@ private:
void messageCallback() override
{
if (auto instance = SharedResourcePointer<TimerThread>::getSharedObjectWithoutCreating())
(*instance)->callTimers();
if (auto* instance = TimerThread::getInstanceWithoutCreating())
instance->callTimers();
}
};
@ -273,9 +272,14 @@ private:
return timers.front().countdownMs;
}
//==============================================================================
TimerThread() : Thread ("JUCE Timer") { timers.reserve (32); }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
};
JUCE_IMPLEMENT_SINGLETON (Timer::TimerThread)
//==============================================================================
Timer::Timer() noexcept {}
Timer::Timer (const Timer&) noexcept {}
@ -299,13 +303,16 @@ void Timer::startTimer (int interval) noexcept
// running, then you're not going to get any timer callbacks!
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
bool wasStopped = (timerPeriodMs == 0);
timerPeriodMs = jmax (1, interval);
if (auto* instance = TimerThread::getInstance())
{
bool wasStopped = (timerPeriodMs == 0);
timerPeriodMs = jmax (1, interval);
if (wasStopped)
timerThread->addTimer (this);
else
timerThread->resetTimerCounter (this);
if (wasStopped)
instance->addTimer (this);
else
instance->resetTimerCounter (this);
}
}
void Timer::startTimerHz (int timerFrequencyHz) noexcept
@ -320,15 +327,17 @@ void Timer::stopTimer() noexcept
{
if (timerPeriodMs > 0)
{
timerThread->removeTimer (this);
if (auto* instance = TimerThread::getInstanceWithoutCreating())
instance->removeTimer (this);
timerPeriodMs = 0;
}
}
void JUCE_CALLTYPE Timer::callPendingTimersSynchronously()
{
if (auto instance = SharedResourcePointer<TimerThread>::getSharedObjectWithoutCreating())
(*instance)->callTimersSynchronously();
if (auto* instance = TimerThread::getInstanceWithoutCreating())
instance->callTimersSynchronously();
}
struct LambdaInvoker final : private Timer,

View file

@ -129,7 +129,6 @@ private:
class TimerThread;
size_t positionInQueue = (size_t) -1;
int timerPeriodMs = 0;
SharedResourcePointer<TimerThread> timerThread;
Timer& operator= (const Timer&) = delete;
};