From 9e9da250eb8518bb25a287a339e1ccba76d00680 Mon Sep 17 00:00:00 2001 From: Anthony Nicholls Date: Wed, 8 May 2024 13:38:45 +0100 Subject: [PATCH] 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. --- modules/juce_events/timers/juce_Timer.cpp | 43 ++++++++++++++--------- modules/juce_events/timers/juce_Timer.h | 1 - 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/modules/juce_events/timers/juce_Timer.cpp b/modules/juce_events/timers/juce_Timer.cpp index 29868f8f91..e0ac72e8d9 100644 --- a/modules/juce_events/timers/juce_Timer.cpp +++ b/modules/juce_events/timers/juce_Timer.cpp @@ -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::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::getSharedObjectWithoutCreating()) - (*instance)->callTimersSynchronously(); + if (auto* instance = TimerThread::getInstanceWithoutCreating()) + instance->callTimersSynchronously(); } struct LambdaInvoker final : private Timer, diff --git a/modules/juce_events/timers/juce_Timer.h b/modules/juce_events/timers/juce_Timer.h index b2c42e21e0..77a6dfdadc 100644 --- a/modules/juce_events/timers/juce_Timer.h +++ b/modules/juce_events/timers/juce_Timer.h @@ -141,7 +141,6 @@ private: class TimerThread; size_t positionInQueue = (size_t) -1; int timerPeriodMs = 0; - SharedResourcePointer timerThread; Timer& operator= (const Timer&) = delete; };