1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

MessageManager: Add callSync counterpart to callAsync

This commit is contained in:
reuk 2025-02-04 12:53:29 +00:00
parent 29cf6ecf04
commit da09b99bbf
No known key found for this signature in database
2 changed files with 61 additions and 39 deletions

View file

@ -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<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;
return callSync ([func, parameter] { return func (parameter); }).value_or (nullptr);
}
//==============================================================================

View file

@ -55,6 +55,11 @@ using MessageCallbackFunction = void* (void* userData);
*/
class JUCE_API MessageManager final
{
template <typename FunctionResult>
using CallSyncResult = std::conditional_t<std::is_same_v<FunctionResult, void>,
bool,
std::optional<FunctionResult>>;
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<T>, where the optional value will be valid if the function
was called successfully, or nullopt otherwise.
*/
template <typename Function>
static auto callSync (Function&& function) -> CallSyncResult<decltype (function())>
{
using FinalResult = CallSyncResult<decltype (function())>;
if (MessageManager::getInstance()->isThisTheMessageThread())
return transformResult (std::forward<Function> (function));
std::promise<FinalResult> 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<Thread::ThreadID> threadWithLock;
mutable std::mutex messageThreadIdMutex;
template <typename Function>
static auto transformResult (Function&& f)
{
if constexpr (std::is_same_v<decltype (f()), void>)
{
f();
return true;
}
else
{
return f();
}
}
static bool postMessageToSystemQueue (MessageBase*);
static void* exitModalLoopCallback (void*);
static void doPlatformSpecificInitialisation();