RunOnObject.h 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. // Copyright 2017 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <QCoreApplication>
  5. #include <QEvent>
  6. #include <QPointer>
  7. #include <QThread>
  8. #include <optional>
  9. #include <type_traits>
  10. #include <utility>
  11. #include "Common/Event.h"
  12. #include "DolphinQt/QtUtils/QueueOnObject.h"
  13. class QObject;
  14. // QWidget and subclasses are not thread-safe! This helper takes arbitrary code from any thread,
  15. // safely runs it on the appropriate GUI thread, waits for it to finish, and returns the result.
  16. //
  17. // If the target object is destructed before the code gets to run, the QPointer will be nulled and
  18. // the function will return nullopt.
  19. template <typename F>
  20. auto RunOnObject(QObject* object, F&& functor)
  21. {
  22. using OptionalResultT = std::optional<std::invoke_result_t<F>>;
  23. // If we queue up a functor on the current thread, it won't run until we return to the event loop,
  24. // which means waiting for it to finish will never complete. Instead, run it immediately.
  25. if (object->thread() == QThread::currentThread())
  26. return OptionalResultT(functor());
  27. class FnInvokeEvent : public QEvent
  28. {
  29. public:
  30. FnInvokeEvent(F&& functor, QObject* obj, Common::Event& event, OptionalResultT& result)
  31. : QEvent(QEvent::None), m_func(std::move(functor)), m_obj(obj), m_event(event),
  32. m_result(result)
  33. {
  34. }
  35. ~FnInvokeEvent()
  36. {
  37. if (m_obj)
  38. {
  39. m_result = m_func();
  40. }
  41. else
  42. {
  43. // is already nullopt
  44. }
  45. m_event.Set();
  46. }
  47. private:
  48. F m_func;
  49. QPointer<QObject> m_obj;
  50. Common::Event& m_event;
  51. OptionalResultT& m_result;
  52. };
  53. Common::Event event{};
  54. OptionalResultT result = std::nullopt;
  55. QCoreApplication::postEvent(object,
  56. new FnInvokeEvent(std::forward<F>(functor), object, event, result));
  57. event.Wait();
  58. return result;
  59. }
  60. template <typename Base, typename Type, typename Receiver>
  61. auto RunOnObject(Receiver* obj, Type Base::*func)
  62. {
  63. return RunOnObject(obj, [obj, func] { return (obj->*func)(); });
  64. }