mirror of
https://github.com/libretro/mgba.git
synced 2024-11-24 00:20:05 +00:00
Qt: Unify worker threads
This commit is contained in:
parent
7ebd2d6e75
commit
c94aff135f
@ -67,10 +67,7 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GBAApp::~GBAApp() {
|
GBAApp::~GBAApp() {
|
||||||
#ifdef USE_SQLITE3
|
m_workerThreads.waitForDone();
|
||||||
m_parseThread.quit();
|
|
||||||
m_parseThread.wait();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBAApp::event(QEvent* event) {
|
bool GBAApp::event(QEvent* event) {
|
||||||
@ -180,14 +177,8 @@ bool GBAApp::reloadGameDB() {
|
|||||||
NoIntroDBDestroy(m_db);
|
NoIntroDBDestroy(m_db);
|
||||||
}
|
}
|
||||||
if (db) {
|
if (db) {
|
||||||
if (m_parseThread.isRunning()) {
|
|
||||||
m_parseThread.quit();
|
|
||||||
m_parseThread.wait();
|
|
||||||
}
|
|
||||||
GameDBParser* parser = new GameDBParser(db);
|
GameDBParser* parser = new GameDBParser(db);
|
||||||
m_parseThread.start();
|
submitWorkerJob(std::bind(&GameDBParser::parseNoIntroDB, parser));
|
||||||
parser->moveToThread(&m_parseThread);
|
|
||||||
QMetaObject::invokeMethod(parser, "parseNoIntroDB");
|
|
||||||
m_db = db;
|
m_db = db;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -199,6 +190,77 @@ bool GBAApp::reloadGameDB() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
qint64 GBAApp::submitWorkerJob(std::function<void ()> job, std::function<void ()> callback) {
|
||||||
|
return submitWorkerJob(job, nullptr, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 GBAApp::submitWorkerJob(std::function<void ()> job, QObject* context, std::function<void ()> callback) {
|
||||||
|
qint64 jobId = m_nextJob;
|
||||||
|
++m_nextJob;
|
||||||
|
WorkerJob* jobRunnable = new WorkerJob(jobId, job, this);
|
||||||
|
m_workerJobs.insert(jobId, jobRunnable);
|
||||||
|
if (callback) {
|
||||||
|
waitOnJob(jobId, context, callback);
|
||||||
|
}
|
||||||
|
m_workerThreads.start(jobRunnable);
|
||||||
|
return jobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GBAApp::removeWorkerJob(qint64 jobId) {
|
||||||
|
for (auto& job : m_workerJobCallbacks.values(jobId)) {
|
||||||
|
disconnect(job);
|
||||||
|
}
|
||||||
|
m_workerJobCallbacks.remove(jobId);
|
||||||
|
if (!m_workerJobs.contains(jobId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||||
|
success = m_workerThreads.tryTake(m_workerJobs[jobId]);
|
||||||
|
#endif
|
||||||
|
if (success) {
|
||||||
|
m_workerJobs.remove(jobId);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool GBAApp::waitOnJob(qint64 jobId, QObject* context, std::function<void ()> callback) {
|
||||||
|
if (!m_workerJobs.contains(jobId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!context) {
|
||||||
|
context = this;
|
||||||
|
}
|
||||||
|
QMetaObject::Connection connection = connect(this, &GBAApp::jobFinished, context, [jobId, callback](qint64 testedJobId) {
|
||||||
|
if (jobId != testedJobId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
m_workerJobCallbacks.insert(m_nextJob, connection);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAApp::finishJob(qint64 jobId) {
|
||||||
|
m_workerJobs.remove(jobId);
|
||||||
|
emit jobFinished(jobId);
|
||||||
|
m_workerJobCallbacks.remove(jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
GBAApp::WorkerJob::WorkerJob(qint64 id, std::function<void ()> job, GBAApp* owner)
|
||||||
|
: m_id(id)
|
||||||
|
, m_job(job)
|
||||||
|
, m_owner(owner)
|
||||||
|
{
|
||||||
|
setAutoDelete(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAApp::WorkerJob::run() {
|
||||||
|
m_job();
|
||||||
|
QMetaObject::invokeMethod(m_owner, "finishJob", Q_ARG(qint64, m_id));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_SQLITE3
|
#ifdef USE_SQLITE3
|
||||||
GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent)
|
GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
@ -8,9 +8,14 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QMultiMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QRunnable>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QThread>
|
#include <QThreadPool>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "CoreManager.h"
|
#include "CoreManager.h"
|
||||||
#include "MultiplayerController.h"
|
#include "MultiplayerController.h"
|
||||||
@ -61,10 +66,34 @@ public:
|
|||||||
const NoIntroDB* gameDB() const { return m_db; }
|
const NoIntroDB* gameDB() const { return m_db; }
|
||||||
bool reloadGameDB();
|
bool reloadGameDB();
|
||||||
|
|
||||||
|
qint64 submitWorkerJob(std::function<void ()> job, std::function<void ()> callback = {});
|
||||||
|
qint64 submitWorkerJob(std::function<void ()> job, QObject* context, std::function<void ()> callback);
|
||||||
|
bool removeWorkerJob(qint64 jobId);
|
||||||
|
bool waitOnJob(qint64 jobId, QObject* context, std::function<void ()> callback);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void jobFinished(qint64 jobId);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool event(QEvent*);
|
bool event(QEvent*);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void finishJob(qint64 jobId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class WorkerJob : public QRunnable {
|
||||||
|
public:
|
||||||
|
WorkerJob(qint64 id, std::function<void ()> job, GBAApp* owner);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
qint64 m_id;
|
||||||
|
std::function<void ()> m_job;
|
||||||
|
GBAApp* m_owner;
|
||||||
|
};
|
||||||
|
|
||||||
Window* newWindowInternal();
|
Window* newWindowInternal();
|
||||||
|
|
||||||
void pauseAll(QList<Window*>* paused);
|
void pauseAll(QList<Window*>* paused);
|
||||||
@ -75,10 +104,12 @@ private:
|
|||||||
MultiplayerController m_multiplayer;
|
MultiplayerController m_multiplayer;
|
||||||
CoreManager m_manager;
|
CoreManager m_manager;
|
||||||
|
|
||||||
|
QMap<qint64, WorkerJob*> m_workerJobs;
|
||||||
|
QMultiMap<qint64, QMetaObject::Connection> m_workerJobCallbacks;
|
||||||
|
QThreadPool m_workerThreads;
|
||||||
|
qint64 m_nextJob = 1;
|
||||||
|
|
||||||
NoIntroDB* m_db = nullptr;
|
NoIntroDB* m_db = nullptr;
|
||||||
#ifdef USE_SQLITE3
|
|
||||||
QThread m_parseThread;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,16 +29,6 @@ void AbstractGameList::removeEntries(QList<LibraryEntryRef> items) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LibraryLoaderThread::LibraryLoaderThread(QObject* parent)
|
|
||||||
: QThread(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryLoaderThread::run() {
|
|
||||||
mLibraryLoadDirectory(m_library, m_directory.toUtf8().constData());
|
|
||||||
m_directory = QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config)
|
LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config)
|
||||||
: QStackedWidget(parent)
|
: QStackedWidget(parent)
|
||||||
, m_config(config)
|
, m_config(config)
|
||||||
@ -47,13 +37,13 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi
|
|||||||
|
|
||||||
if (!path.isNull()) {
|
if (!path.isNull()) {
|
||||||
// This can return NULL if the library is already open
|
// This can return NULL if the library is already open
|
||||||
m_library = mLibraryLoad(path.toUtf8().constData());
|
m_library = std::shared_ptr<mLibrary>(mLibraryLoad(path.toUtf8().constData()), mLibraryDestroy);
|
||||||
}
|
}
|
||||||
if (!m_library) {
|
if (!m_library) {
|
||||||
m_library = mLibraryCreateEmpty();
|
m_library = std::shared_ptr<mLibrary>(mLibraryCreateEmpty(), mLibraryDestroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
mLibraryAttachGameDB(m_library, GBAApp::app()->gameDB());
|
mLibraryAttachGameDB(m_library.get(), GBAApp::app()->gameDB());
|
||||||
|
|
||||||
m_libraryTree = new LibraryTree(this);
|
m_libraryTree = new LibraryTree(this);
|
||||||
addWidget(m_libraryTree->widget());
|
addWidget(m_libraryTree->widget());
|
||||||
@ -61,25 +51,12 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi
|
|||||||
m_libraryGrid = new LibraryGrid(this);
|
m_libraryGrid = new LibraryGrid(this);
|
||||||
addWidget(m_libraryGrid->widget());
|
addWidget(m_libraryGrid->widget());
|
||||||
|
|
||||||
connect(&m_loaderThread, &QThread::finished, this, &LibraryController::refresh, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
setViewStyle(LibraryStyle::STYLE_LIST);
|
setViewStyle(LibraryStyle::STYLE_LIST);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
LibraryController::~LibraryController() {
|
LibraryController::~LibraryController() {
|
||||||
mLibraryListingDeinit(&m_listing);
|
mLibraryListingDeinit(&m_listing);
|
||||||
|
|
||||||
if (m_loaderThread.isRunning()) {
|
|
||||||
m_loaderThread.wait();
|
|
||||||
}
|
|
||||||
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
|
|
||||||
m_library = m_loaderThread.m_library;
|
|
||||||
m_loaderThread.m_library = nullptr;
|
|
||||||
}
|
|
||||||
if (m_library) {
|
|
||||||
mLibraryDestroy(m_library);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibraryController::setViewStyle(LibraryStyle newStyle) {
|
void LibraryController::setViewStyle(LibraryStyle newStyle) {
|
||||||
@ -117,7 +94,7 @@ LibraryEntryRef LibraryController::selectedEntry() {
|
|||||||
VFile* LibraryController::selectedVFile() {
|
VFile* LibraryController::selectedVFile() {
|
||||||
LibraryEntryRef entry = selectedEntry();
|
LibraryEntryRef entry = selectedEntry();
|
||||||
if (entry) {
|
if (entry) {
|
||||||
return mLibraryOpenVFile(m_library, entry->entry);
|
return mLibraryOpenVFile(m_library.get(), entry->entry);
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -129,35 +106,26 @@ QPair<QString, QString> LibraryController::selectedPath() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LibraryController::addDirectory(const QString& dir) {
|
void LibraryController::addDirectory(const QString& dir) {
|
||||||
m_loaderThread.m_directory = dir;
|
// The worker thread temporarily owns the library
|
||||||
m_loaderThread.m_library = m_library;
|
std::shared_ptr<mLibrary> library = m_library;
|
||||||
// The m_loaderThread temporarily owns the library
|
m_libraryJob = GBAApp::app()->submitWorkerJob(std::bind(&LibraryController::loadDirectory, this, dir), this, [this, library]() {
|
||||||
m_library = nullptr;
|
m_libraryJob = -1;
|
||||||
m_loaderThread.start();
|
refresh();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibraryController::clear() {
|
void LibraryController::clear() {
|
||||||
if (!m_library) {
|
if (m_libraryJob > 0) {
|
||||||
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
|
return;
|
||||||
m_library = m_loaderThread.m_library;
|
|
||||||
m_loaderThread.m_library = nullptr;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mLibraryClear(m_library);
|
mLibraryClear(m_library.get());
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibraryController::refresh() {
|
void LibraryController::refresh() {
|
||||||
if (!m_library) {
|
if (m_libraryJob > 0) {
|
||||||
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
|
return;
|
||||||
m_library = m_loaderThread.m_library;
|
|
||||||
m_loaderThread.m_library = nullptr;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDisabled(true);
|
setDisabled(true);
|
||||||
@ -166,7 +134,7 @@ void LibraryController::refresh() {
|
|||||||
QList<LibraryEntryRef> newEntries;
|
QList<LibraryEntryRef> newEntries;
|
||||||
|
|
||||||
mLibraryListingClear(&m_listing);
|
mLibraryListingClear(&m_listing);
|
||||||
mLibraryGetEntries(m_library, &m_listing, 0, 0, nullptr);
|
mLibraryGetEntries(m_library.get(), &m_listing, 0, 0, nullptr);
|
||||||
for (size_t i = 0; i < mLibraryListingSize(&m_listing); i++) {
|
for (size_t i = 0; i < mLibraryListingSize(&m_listing); i++) {
|
||||||
mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i);
|
mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i);
|
||||||
QString fullpath = QString("%1/%2").arg(entry->base, entry->filename);
|
QString fullpath = QString("%1/%2").arg(entry->base, entry->filename);
|
||||||
@ -210,4 +178,10 @@ void LibraryController::selectLastBootedGame() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LibraryController::loadDirectory(const QString& dir) {
|
||||||
|
// This class can get delted during this function (sigh) so we need to hold onto this
|
||||||
|
std::shared_ptr<mLibrary> library = m_library;
|
||||||
|
mLibraryLoadDirectory(library.get(), dir.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QThread>
|
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
|
|
||||||
#include <mgba/core/library.h>
|
#include <mgba/core/library.h>
|
||||||
@ -66,19 +65,6 @@ public:
|
|||||||
virtual QWidget* widget() = 0;
|
virtual QWidget* widget() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LibraryLoaderThread final : public QThread {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
LibraryLoaderThread(QObject* parent = nullptr);
|
|
||||||
|
|
||||||
mLibrary* m_library = nullptr;
|
|
||||||
QString m_directory;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void run() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LibraryController final : public QStackedWidget {
|
class LibraryController final : public QStackedWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -110,9 +96,11 @@ private slots:
|
|||||||
void refresh();
|
void refresh();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void loadDirectory(const QString&); // Called on separate thread
|
||||||
|
|
||||||
ConfigController* m_config = nullptr;
|
ConfigController* m_config = nullptr;
|
||||||
LibraryLoaderThread m_loaderThread;
|
std::shared_ptr<mLibrary> m_library;
|
||||||
mLibrary* m_library = nullptr;
|
qint64 m_libraryJob = -1;
|
||||||
mLibraryListing m_listing;
|
mLibraryListing m_listing;
|
||||||
QMap<QString, LibraryEntryRef> m_entries;
|
QMap<QString, LibraryEntryRef> m_entries;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user