// Copyright 2017 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #pragma once #include #include #include #include #include #include #include #include "Common/Event.h" #include "DolphinQt/QtUtils/QueueOnObject.h" class QObject; // 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. template auto RunOnObject(QObject* object, F&& functor) { using OptionalResultT = std::optional>; // 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()); 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(); return result; } template auto RunOnObject(Receiver* obj, Type Base::*func) { return RunOnObject(obj, [obj, func] { return (obj->*func)(); }); }