1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +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
{
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
@ -216,8 +215,8 @@ private:
void messageCallback() override
{
if (auto instance = SharedResourcePointer<TimerThread>::getSharedObjectWithoutCreating())
(*instance)->callTimers();
if (auto* instance = TimerThread::getInstanceWithoutCreating())
instance->callTimers();
}
};
@ -285,9 +284,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 {}
@ -311,13 +315,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
@ -332,15 +339,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

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