| @@ -31,14 +31,15 @@ public: | |||
| TimerThread() | |||
| : Thread ("Juce Timer"), | |||
| firstTimer (nullptr), | |||
| callbackNeeded (0) | |||
| firstTimer (nullptr) | |||
| { | |||
| triggerAsyncUpdate(); | |||
| } | |||
| ~TimerThread() noexcept | |||
| { | |||
| signalThreadShouldExit(); | |||
| callbackArrived.signal(); | |||
| stopThread (4000); | |||
| jassert (instance == this || instance == nullptr); | |||
| @@ -63,46 +64,29 @@ public: | |||
| if (timeUntilFirstTimer <= 0) | |||
| { | |||
| /* If we managed to set the atomic boolean to true then send a message, this is needed | |||
| as a memory barrier so the message won't be sent before callbackNeeded is set to true, | |||
| but if it fails it means the message-thread changed the value from under us so at least | |||
| some processing is happenening and we can just loop around and try again | |||
| */ | |||
| if (callbackNeeded.compareAndSetBool (1, 0)) | |||
| if (callbackArrived.wait (0)) | |||
| { | |||
| // already a message in flight - do nothing.. | |||
| } | |||
| else | |||
| { | |||
| messageToSend->post(); | |||
| /* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS | |||
| when the app has a modal loop), so this is how long to wait before assuming the | |||
| message has been lost and trying again. | |||
| */ | |||
| const uint32 messageDeliveryTimeout = now + 300; | |||
| while (callbackNeeded.get() != 0) | |||
| if (! callbackArrived.wait (300)) | |||
| { | |||
| wait (4); | |||
| if (threadShouldExit()) | |||
| return; | |||
| if (Time::getMillisecondCounter() > messageDeliveryTimeout) | |||
| { | |||
| messageToSend->post(); | |||
| break; | |||
| } | |||
| // Sometimes our message can get discarded by the OS (e.g. when running as an RTAS | |||
| // when the app has a modal loop), so this is how long to wait before assuming the | |||
| // message has been lost and trying again. | |||
| messageToSend->post(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| wait (1); | |||
| continue; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| // don't wait for too long because running this loop also helps keep the | |||
| // Time::getApproximateMillisecondTimer value stay up-to-date | |||
| wait (jlimit (1, 50, timeUntilFirstTimer)); | |||
| } | |||
| // don't wait for too long because running this loop also helps keep the | |||
| // Time::getApproximateMillisecondTimer value stay up-to-date | |||
| wait (jlimit (1, 100, timeUntilFirstTimer)); | |||
| } | |||
| } | |||
| @@ -127,13 +111,7 @@ public: | |||
| JUCE_CATCH_EXCEPTION | |||
| } | |||
| /* This is needed as a memory barrier to make sure all processing of current timers is done | |||
| before the boolean is set. This set should never fail since if it was false in the first place, | |||
| we wouldn't get a message (so it can't be changed from false to true from under us), and if we | |||
| get a message then the value is true and the other thread can only set it to true again and | |||
| we will get another callback to set it to false. | |||
| */ | |||
| callbackNeeded.set (0); | |||
| callbackArrived.signal(); | |||
| } | |||
| void callTimersSynchronously() | |||
| @@ -184,7 +162,7 @@ public: | |||
| private: | |||
| Timer* volatile firstTimer; | |||
| Atomic<int> callbackNeeded; | |||
| WaitableEvent callbackArrived; | |||
| struct CallTimersMessage : public MessageManager::MessageBase | |||
| { | |||