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:
parent
5b98bfdc0f
commit
bbc63ba151
2 changed files with 26 additions and 18 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ private:
|
|||
class TimerThread;
|
||||
size_t positionInQueue = (size_t) -1;
|
||||
int timerPeriodMs = 0;
|
||||
SharedResourcePointer<TimerThread> timerThread;
|
||||
|
||||
Timer& operator= (const Timer&) = delete;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue