1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +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 7b4f71e700
commit 9e9da250eb
2 changed files with 26 additions and 18 deletions

View file

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

View file

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