|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- MessageManager::MessageManager() noexcept
- : messageThreadId (Thread::getCurrentThreadId())
- {
- JUCE_VERSION_ID
-
- if (JUCEApplicationBase::isStandaloneApp())
- Thread::setCurrentThreadName ("JUCE Message Thread");
- }
-
- MessageManager::~MessageManager() noexcept
- {
- broadcaster.reset();
-
- doPlatformSpecificShutdown();
-
- jassert (instance == this);
- instance = nullptr; // do this last in case this instance is still needed by doPlatformSpecificShutdown()
- }
-
- MessageManager* MessageManager::instance = nullptr;
-
- MessageManager* MessageManager::getInstance()
- {
- if (instance == nullptr)
- {
- instance = new MessageManager();
- doPlatformSpecificInitialisation();
- }
-
- return instance;
- }
-
- MessageManager* MessageManager::getInstanceWithoutCreating() noexcept
- {
- return instance;
- }
-
- void MessageManager::deleteInstance()
- {
- deleteAndZero (instance);
- }
-
- //==============================================================================
- bool MessageManager::MessageBase::post()
- {
- auto* mm = MessageManager::instance;
-
- if (mm == nullptr || mm->quitMessagePosted.get() != 0 || ! postMessageToSystemQueue (this))
- {
- Ptr deleter (this); // (this will delete messages that were just created with a 0 ref count)
- return false;
- }
-
- return true;
- }
-
- //==============================================================================
- #if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID)
- // implemented in platform-specific code (juce_linux_Messaging.cpp and juce_win32_Messaging.cpp)
- bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages);
-
- class MessageManager::QuitMessage : public MessageManager::MessageBase
- {
- public:
- QuitMessage() {}
-
- void messageCallback() override
- {
- if (auto* mm = MessageManager::instance)
- mm->quitMessageReceived = true;
- }
-
- JUCE_DECLARE_NON_COPYABLE (QuitMessage)
- };
-
- void MessageManager::runDispatchLoop()
- {
- jassert (isThisTheMessageThread()); // must only be called by the message thread
-
- while (quitMessageReceived.get() == 0)
- {
- JUCE_TRY
- {
- if (! dispatchNextMessageOnSystemQueue (false))
- Thread::sleep (1);
- }
- JUCE_CATCH_EXCEPTION
- }
- }
-
- void MessageManager::stopDispatchLoop()
- {
- (new QuitMessage())->post();
- quitMessagePosted = true;
- }
-
- #if JUCE_MODAL_LOOPS_PERMITTED
- bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
- {
- jassert (isThisTheMessageThread()); // must only be called by the message thread
-
- auto endTime = Time::currentTimeMillis() + millisecondsToRunFor;
-
- while (quitMessageReceived.get() == 0)
- {
- JUCE_TRY
- {
- if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0))
- Thread::sleep (1);
- }
- JUCE_CATCH_EXCEPTION
-
- if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime)
- break;
- }
-
- return quitMessageReceived.get() == 0;
- }
- #endif
-
- #endif
-
- //==============================================================================
- class AsyncFunctionCallback : public MessageManager::MessageBase
- {
- public:
- AsyncFunctionCallback (MessageCallbackFunction* const f, void* const param)
- : func (f), parameter (param)
- {}
-
- void messageCallback() override
- {
- result = (*func) (parameter);
- finished.signal();
- }
-
- WaitableEvent finished;
- std::atomic<void*> result { nullptr };
-
- private:
- MessageCallbackFunction* const func;
- void* const parameter;
-
- JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback)
- };
-
- void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter)
- {
- if (isThisTheMessageThread())
- return func (parameter);
-
- // If this thread has the message manager locked, then this will deadlock!
- jassert (! currentThreadHasLockedMessageManager());
-
- const ReferenceCountedObjectPtr<AsyncFunctionCallback> message (new AsyncFunctionCallback (func, parameter));
-
- if (message->post())
- {
- message->finished.wait();
- return message->result.load();
- }
-
- jassertfalse; // the OS message queue failed to send the message!
- return nullptr;
- }
-
- bool MessageManager::callAsync (std::function<void()> fn)
- {
- struct AsyncCallInvoker : public MessageBase
- {
- AsyncCallInvoker (std::function<void()> f) : callback (std::move (f)) {}
- void messageCallback() override { callback(); }
- std::function<void()> callback;
- };
-
- return (new AsyncCallInvoker (std::move (fn)))->post();
- }
-
- //==============================================================================
- void MessageManager::deliverBroadcastMessage (const String& value)
- {
- if (broadcaster != nullptr)
- broadcaster->sendActionMessage (value);
- }
-
- void MessageManager::registerBroadcastListener (ActionListener* const listener)
- {
- if (broadcaster == nullptr)
- broadcaster.reset (new ActionBroadcaster());
-
- broadcaster->addActionListener (listener);
- }
-
- void MessageManager::deregisterBroadcastListener (ActionListener* const listener)
- {
- if (broadcaster != nullptr)
- broadcaster->removeActionListener (listener);
- }
-
- //==============================================================================
- bool MessageManager::isThisTheMessageThread() const noexcept
- {
- return Thread::getCurrentThreadId() == messageThreadId;
- }
-
- void MessageManager::setCurrentThreadAsMessageThread()
- {
- auto thisThread = Thread::getCurrentThreadId();
-
- if (messageThreadId != thisThread)
- {
- messageThreadId = thisThread;
-
- #if JUCE_WINDOWS
- // This is needed on windows to make sure the message window is created by this thread
- doPlatformSpecificShutdown();
- doPlatformSpecificInitialisation();
- #endif
- }
- }
-
- bool MessageManager::currentThreadHasLockedMessageManager() const noexcept
- {
- auto thisThread = Thread::getCurrentThreadId();
- return thisThread == messageThreadId || thisThread == threadWithLock.get();
- }
-
- bool MessageManager::existsAndIsLockedByCurrentThread() noexcept
- {
- if (auto i = getInstanceWithoutCreating())
- return i->currentThreadHasLockedMessageManager();
-
- return false;
- }
-
- bool MessageManager::existsAndIsCurrentThread() noexcept
- {
- if (auto i = getInstanceWithoutCreating())
- return i->isThisTheMessageThread();
-
- return false;
- }
-
- //==============================================================================
- //==============================================================================
- /* The only safe way to lock the message thread while another thread does
- some work is by posting a special message, whose purpose is to tie up the event
- loop until the other thread has finished its business.
-
- Any other approach can get horribly deadlocked if the OS uses its own hidden locks which
- get locked before making an event callback, because if the same OS lock gets indirectly
- accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens
- in Cocoa).
- */
- struct MessageManager::Lock::BlockingMessage : public MessageManager::MessageBase
- {
- BlockingMessage (const MessageManager::Lock* parent) noexcept
- : owner (parent)
- {}
-
- void messageCallback() override
- {
- {
- ScopedLock lock (ownerCriticalSection);
-
- if (auto* o = owner.get())
- o->messageCallback();
- }
-
- releaseEvent.wait();
- }
-
- CriticalSection ownerCriticalSection;
- Atomic<const MessageManager::Lock*> owner;
- WaitableEvent releaseEvent;
-
- JUCE_DECLARE_NON_COPYABLE (BlockingMessage)
- };
-
- //==============================================================================
- MessageManager::Lock::Lock() {}
- MessageManager::Lock::~Lock() { exit(); }
- void MessageManager::Lock::enter() const noexcept { tryAcquire (true); }
- bool MessageManager::Lock::tryEnter() const noexcept { return tryAcquire (false); }
-
- bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept
- {
- auto* mm = MessageManager::instance;
-
- if (mm == nullptr)
- {
- jassertfalse;
- return false;
- }
-
- if (! lockIsMandatory && (abortWait.get() != 0))
- {
- abortWait.set (0);
- return false;
- }
-
- if (mm->currentThreadHasLockedMessageManager())
- return true;
-
- try
- {
- blockingMessage = *new BlockingMessage (this);
- }
- catch (...)
- {
- jassert (! lockIsMandatory);
- return false;
- }
-
- if (! blockingMessage->post())
- {
- // post of message failed while trying to get the lock
- jassert (! lockIsMandatory);
- blockingMessage = nullptr;
- return false;
- }
-
- do
- {
- while (abortWait.get() == 0)
- lockedEvent.wait (-1);
-
- abortWait.set (0);
-
- if (lockGained.get() != 0)
- {
- mm->threadWithLock = Thread::getCurrentThreadId();
- return true;
- }
-
- } while (lockIsMandatory);
-
- // we didn't get the lock
- blockingMessage->releaseEvent.signal();
-
- {
- ScopedLock lock (blockingMessage->ownerCriticalSection);
-
- lockGained.set (0);
- blockingMessage->owner.set (nullptr);
- }
-
- blockingMessage = nullptr;
- return false;
- }
-
- void MessageManager::Lock::exit() const noexcept
- {
- if (lockGained.compareAndSetBool (false, true))
- {
- auto* mm = MessageManager::instance;
-
- jassert (mm == nullptr || mm->currentThreadHasLockedMessageManager());
- lockGained.set (0);
-
- if (mm != nullptr)
- mm->threadWithLock = {};
-
- if (blockingMessage != nullptr)
- {
- blockingMessage->releaseEvent.signal();
- blockingMessage = nullptr;
- }
- }
- }
-
- void MessageManager::Lock::messageCallback() const
- {
- lockGained.set (1);
- abort();
- }
-
- void MessageManager::Lock::abort() const noexcept
- {
- abortWait.set (1);
- lockedEvent.signal();
- }
-
- //==============================================================================
- MessageManagerLock::MessageManagerLock (Thread* threadToCheck)
- : locked (attemptLock (threadToCheck, nullptr))
- {}
-
- MessageManagerLock::MessageManagerLock (ThreadPoolJob* jobToCheck)
- : locked (attemptLock (nullptr, jobToCheck))
- {}
-
- bool MessageManagerLock::attemptLock (Thread* threadToCheck, ThreadPoolJob* jobToCheck)
- {
- jassert (threadToCheck == nullptr || jobToCheck == nullptr);
-
- if (threadToCheck != nullptr)
- threadToCheck->addListener (this);
-
- if (jobToCheck != nullptr)
- jobToCheck->addListener (this);
-
- // tryEnter may have a spurious abort (return false) so keep checking the condition
- while ((threadToCheck == nullptr || ! threadToCheck->threadShouldExit())
- && (jobToCheck == nullptr || ! jobToCheck->shouldExit()))
- {
- if (mmLock.tryEnter())
- break;
- }
-
- if (threadToCheck != nullptr)
- {
- threadToCheck->removeListener (this);
-
- if (threadToCheck->threadShouldExit())
- return false;
- }
-
- if (jobToCheck != nullptr)
- {
- jobToCheck->removeListener (this);
-
- if (jobToCheck->shouldExit())
- return false;
- }
-
- return true;
- }
-
- MessageManagerLock::~MessageManagerLock() { mmLock.exit(); }
-
- void MessageManagerLock::exitSignalSent()
- {
- mmLock.abort();
- }
-
- //==============================================================================
- JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI()
- {
- JUCE_AUTORELEASEPOOL
- {
- MessageManager::getInstance();
- }
- }
-
- JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI()
- {
- JUCE_AUTORELEASEPOOL
- {
- DeletedAtShutdown::deleteAll();
- MessageManager::deleteInstance();
- }
- }
-
- static int numScopedInitInstances = 0;
-
- ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { if (numScopedInitInstances++ == 0) initialiseJuce_GUI(); }
- ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { if (--numScopedInitInstances == 0) shutdownJuce_GUI(); }
-
- } // namespace juce
|