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:
parent
7b4f71e700
commit
9e9da250eb
2 changed files with 26 additions and 18 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -141,7 +141,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