mirror of
				https://github.com/dolphin-emu/dolphin.git
				synced 2025-10-25 01:19:19 +00:00 
			
		
		
		
	SPDX standardizes how source code conveys its copyright and licensing information. See https://spdx.github.io/spdx-spec/1-rationale/ . SPDX tags are adopted in many large projects, including things like the Linux kernel.
		
			
				
	
	
		
			76 lines
		
	
	
	
		
			2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			76 lines
		
	
	
	
		
			2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2017 Dolphin Emulator Project
 | |
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <QCoreApplication>
 | |
| #include <QEvent>
 | |
| #include <QPointer>
 | |
| #include <QThread>
 | |
| #include <optional>
 | |
| #include <type_traits>
 | |
| #include <utility>
 | |
| 
 | |
| #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 <typename F>
 | |
| auto RunOnObject(QObject* object, F&& functor)
 | |
| {
 | |
|   using OptionalResultT = std::optional<std::invoke_result_t<F>>;
 | |
| 
 | |
|   // 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<QObject> m_obj;
 | |
|     Common::Event& m_event;
 | |
|     OptionalResultT& m_result;
 | |
|   };
 | |
| 
 | |
|   Common::Event event{};
 | |
|   OptionalResultT result = std::nullopt;
 | |
|   QCoreApplication::postEvent(object,
 | |
|                               new FnInvokeEvent(std::forward<F>(functor), object, event, result));
 | |
|   event.Wait();
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| template <typename Base, typename Type, typename Receiver>
 | |
| auto RunOnObject(Receiver* obj, Type Base::*func)
 | |
| {
 | |
|   return RunOnObject(obj, [obj, func] { return (obj->*func)(); });
 | |
| }
 |