From 5a6fce31b2fcfefd83509c8898fac6dc327b68bf Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 16 Nov 2025 23:40:48 -0600 Subject: [PATCH] DolphinQt/QtUtils: Simplify RunOnObject and eliminate Common::Event race. --- Source/Core/DolphinQt/QtUtils/RunOnObject.h | 70 +++++---------------- 1 file changed, 15 insertions(+), 55 deletions(-) diff --git a/Source/Core/DolphinQt/QtUtils/RunOnObject.h b/Source/Core/DolphinQt/QtUtils/RunOnObject.h index 73e6c05beed..dfa6bbbf915 100644 --- a/Source/Core/DolphinQt/QtUtils/RunOnObject.h +++ b/Source/Core/DolphinQt/QtUtils/RunOnObject.h @@ -3,74 +3,34 @@ #pragma once -#include -#include -#include -#include +#include +#include #include -#include -#include -#include "Common/Event.h" -#include "DolphinQt/QtUtils/QueueOnObject.h" +#include -class QObject; +#include "Common/OneShotEvent.h" // QWidget and subclasses are not thread-safe! This helper takes arbitrary code from any thread, // safely runs it on the appropriate GUI thread, waits for it to finish, and returns the result. // -// If the target object is destructed before the code gets to run, the QPointer will be nulled and -// the function will return nullopt. +// If the target object is destructed before the code gets to run the function will return nullopt. -template -auto RunOnObject(QObject* object, F&& functor) +auto RunOnObject(QObject* object, std::invocable<> auto&& functor) { - using OptionalResultT = std::optional>; + Common::OneShotEvent task_complete; + std::optional result; - // If we queue up a functor on the current thread, it won't run until we return to the event loop, - // which means waiting for it to finish will never complete. Instead, run it immediately. - if (object->thread() == QThread::currentThread()) - return OptionalResultT(functor()); + QMetaObject::invokeMethod( + object, [&, guard = Common::ScopedSetter{&task_complete}] { result = functor(); }); - class FnInvokeEvent : public QEvent - { - public: - FnInvokeEvent(F&& functor, QObject* obj, Common::Event& event, OptionalResultT& result) - : QEvent(QEvent::None), m_func(std::move(functor)), m_obj(obj), m_event(event), - m_result(result) - { - } - - ~FnInvokeEvent() - { - if (m_obj) - { - m_result = m_func(); - } - else - { - // is already nullopt - } - m_event.Set(); - } - - private: - F m_func; - QPointer m_obj; - Common::Event& m_event; - OptionalResultT& m_result; - }; - - Common::Event event{}; - OptionalResultT result = std::nullopt; - QCoreApplication::postEvent(object, - new FnInvokeEvent(std::forward(functor), object, event, result)); - event.Wait(); + // Wait for the lambda to go out of scope. The result may or may not have been assigned. + task_complete.Wait(); return result; } -template -auto RunOnObject(Receiver* obj, Type Base::* func) +template Receiver> +auto RunOnObject(Receiver* obj, auto Receiver::* func) { - return RunOnObject(obj, [obj, func] { return (obj->*func)(); }); + return RunOnObject(obj, std::bind(func, obj)); }