diff options
| author | spycrab <spycrab@users.noreply.github.com> | 2018-05-22 01:03:20 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-22 01:03:20 +0200 |
| commit | 302093d777c895ee7b04df5f02de347d65622b52 (patch) | |
| tree | cd1d10ab07f47a2a9cce7069c558bc7cb6324ca2 /Source/Core | |
| parent | f31f1a08fb8dca9e51e79d1d3a65ee8bff38273b (diff) | |
| parent | 268b424843c859d5d063dfa2da8b3edd7670cddc (diff) | |
Merge pull request #6934 from riking/runonobject
QtUtils/RunOnObject: Make safe under object destruction
Diffstat (limited to 'Source/Core')
| -rw-r--r-- | Source/Core/DolphinQt2/Main.cpp | 5 | ||||
| -rw-r--r-- | Source/Core/DolphinQt2/MainWindow.cpp | 5 | ||||
| -rw-r--r-- | Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp | 10 | ||||
| -rw-r--r-- | Source/Core/DolphinQt2/QtUtils/RunOnObject.h | 52 | ||||
| -rw-r--r-- | Source/Core/DolphinQt2/Updater.cpp | 4 |
5 files changed, 62 insertions, 14 deletions
diff --git a/Source/Core/DolphinQt2/Main.cpp b/Source/Core/DolphinQt2/Main.cpp index 95593ebe34..c3c62ca06f 100644 --- a/Source/Core/DolphinQt2/Main.cpp +++ b/Source/Core/DolphinQt2/Main.cpp @@ -34,7 +34,7 @@ static bool QtMsgAlertHandler(const char* caption, const char* text, bool yes_no, MsgType style) { - return RunOnObject(QApplication::instance(), [&] { + std::optional<bool> r = RunOnObject(QApplication::instance(), [&] { QMessageBox message_box(QApplication::activeWindow()); message_box.setWindowTitle(QString::fromUtf8(caption)); message_box.setText(QString::fromUtf8(text)); @@ -68,6 +68,9 @@ static bool QtMsgAlertHandler(const char* caption, const char* text, bool yes_no return false; }); + if (r.has_value()) + return *r; + return false; } // N.B. On Windows, this should be called from WinMain. Link against qtmain and specify diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index 78ac8725e5..90bf1b2d20 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -1196,13 +1196,16 @@ void MainWindow::OnImportNANDBackup() }); }, [this] { - return RunOnObject(this, [this] { + std::optional<std::string> keys_file = RunOnObject(this, [this] { return QFileDialog::getOpenFileName(this, tr("Select the keys file (OTP/SEEPROM dump)"), QDir::currentPath(), tr("BootMii keys file (*.bin);;" "All Files (*)")) .toStdString(); }); + if (keys_file) + return *keys_file; + return std::string(""); }); QueueOnObject(dialog, &QProgressDialog::close); }); diff --git a/Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp index 8dcd4df9db..e6d7c29800 100644 --- a/Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp @@ -580,12 +580,15 @@ void NetPlayDialog::OnTraversalError(TraversalClient::FailureReason error) bool NetPlayDialog::IsRecording() { - return RunOnObject(m_record_input_box, &QCheckBox::isChecked); + std::optional<bool> is_recording = RunOnObject(m_record_input_box, &QCheckBox::isChecked); + if (is_recording) + return *is_recording; + return false; } std::string NetPlayDialog::FindGame(const std::string& game) { - return RunOnObject(this, [this, game] { + std::optional<std::string> path = RunOnObject(this, [this, game] { for (int i = 0; i < m_game_list_model->rowCount(QModelIndex()); i++) { if (m_game_list_model->GetUniqueIdentifier(i).toStdString() == game) @@ -593,6 +596,9 @@ std::string NetPlayDialog::FindGame(const std::string& game) } return std::string(""); }); + if (path) + return *path; + return std::string(""); } void NetPlayDialog::ShowMD5Dialog(const std::string& file_identifier) diff --git a/Source/Core/DolphinQt2/QtUtils/RunOnObject.h b/Source/Core/DolphinQt2/QtUtils/RunOnObject.h index bd0b2ca556..2b016db3f3 100644 --- a/Source/Core/DolphinQt2/QtUtils/RunOnObject.h +++ b/Source/Core/DolphinQt2/QtUtils/RunOnObject.h @@ -4,7 +4,11 @@ #pragma once +#include <QCoreApplication> +#include <QEvent> +#include <QPointer> #include <QThread> +#include <optional> #include <type_traits> #include <utility> @@ -15,21 +19,53 @@ 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::result_of_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 functor(); - - Common::Event event; - std::result_of_t<F()> result; - QueueOnObject(object, [&event, &result, functor = std::forward<F>(functor) ] { - result = functor(); - event.Set(); - }); + 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; } diff --git a/Source/Core/DolphinQt2/Updater.cpp b/Source/Core/DolphinQt2/Updater.cpp index 918119bcf0..6ef92a6b0b 100644 --- a/Source/Core/DolphinQt2/Updater.cpp +++ b/Source/Core/DolphinQt2/Updater.cpp @@ -30,7 +30,7 @@ void Updater::OnUpdateAvailable(const NewVersionInformation& info) { bool later = false; - int choice = RunOnObject(m_parent, [&] { + std::optional<int> choice = RunOnObject(m_parent, [&] { QDialog* dialog = new QDialog(m_parent); dialog->setWindowTitle(tr("Update available")); dialog->setWindowFlags(dialog->windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -79,7 +79,7 @@ void Updater::OnUpdateAvailable(const NewVersionInformation& info) return dialog->exec(); }); - if (choice == QDialog::Accepted) + if (choice && *choice == QDialog::Accepted) { TriggerUpdate(info, later ? AutoUpdateChecker::RestartMode::NO_RESTART_AFTER_UPDATE : AutoUpdateChecker::RestartMode::RESTART_AFTER_UPDATE); |
