diff --git a/modules/juce_events/messages/juce_MessageManager.cpp b/modules/juce_events/messages/juce_MessageManager.cpp index f0ddf6127d..244d4ff017 100644 --- a/modules/juce_events/messages/juce_MessageManager.cpp +++ b/modules/juce_events/messages/juce_MessageManager.cpp @@ -161,47 +161,9 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) #endif //============================================================================== -class AsyncFunctionCallback final : 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 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 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; + return callSync ([func, parameter] { return func (parameter); }).value_or (nullptr); } //============================================================================== diff --git a/modules/juce_events/messages/juce_MessageManager.h b/modules/juce_events/messages/juce_MessageManager.h index 904bb227e9..0c63acc209 100644 --- a/modules/juce_events/messages/juce_MessageManager.h +++ b/modules/juce_events/messages/juce_MessageManager.h @@ -55,6 +55,11 @@ using MessageCallbackFunction = void* (void* userData); */ class JUCE_API MessageManager final { + template + using CallSyncResult = std::conditional_t, + bool, + std::optional>; + public: //============================================================================== /** Returns the global instance of the MessageManager. */ @@ -142,6 +147,47 @@ public: */ void* callFunctionOnMessageThread (MessageCallbackFunction* callback, void* userData); + /** Similar to callFunctionOnMessageThread(), calls a function on the message thread, + blocking the current thread until a result is available. + + Be careful not to cause any deadlocks with this! It's easy to do - e.g. if the caller + thread has a critical section locked, which an unrelated message callback then tries to lock + before the message thread gets round to processing this callback. + + @param function the function to call, which should have no parameters + @returns if function() returns void, then callSync returns a boolean where + 'true' indicates that the function was called successfully, and 'false' + indicates that the message could not be posted. + if function() returns any other type 'T', then callSync returns + std::optional, where the optional value will be valid if the function + was called successfully, or nullopt otherwise. + */ + template + static auto callSync (Function&& function) -> CallSyncResult + { + using FinalResult = CallSyncResult; + + if (MessageManager::getInstance()->isThisTheMessageThread()) + return transformResult (std::forward (function)); + + std::promise promise; + auto future = promise.get_future(); + + const auto sent = callAsync ([p = std::move (promise), fn = std::move (function)]() mutable + { + p.set_value (transformResult (std::move (fn))); + }); + + if (! sent) + { + // Failed to post message! + jassertfalse; + return {}; + } + + return future.get(); + } + /** Returns true if the caller-thread is the message thread. */ bool isThisTheMessageThread() const noexcept; @@ -366,6 +412,20 @@ private: Atomic threadWithLock; mutable std::mutex messageThreadIdMutex; + template + static auto transformResult (Function&& f) + { + if constexpr (std::is_same_v) + { + f(); + return true; + } + else + { + return f(); + } + } + static bool postMessageToSystemQueue (MessageBase*); static void* exitModalLoopCallback (void*); static void doPlatformSpecificInitialisation();