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() {
|
||||
#ifdef USE_SQLITE3
|
||||
m_parseThread.quit();
|
||||
m_parseThread.wait();
|
||||
#endif
|
||||
m_workerThreads.waitForDone();
|
||||
}
|
||||
|
||||
bool GBAApp::event(QEvent* event) {
|
||||
@ -180,14 +177,8 @@ bool GBAApp::reloadGameDB() {
|
||||
NoIntroDBDestroy(m_db);
|
||||
}
|
||||
if (db) {
|
||||
if (m_parseThread.isRunning()) {
|
||||
m_parseThread.quit();
|
||||
m_parseThread.wait();
|
||||
}
|
||||
GameDBParser* parser = new GameDBParser(db);
|
||||
m_parseThread.start();
|
||||
parser->moveToThread(&m_parseThread);
|
||||
QMetaObject::invokeMethod(parser, "parseNoIntroDB");
|
||||
submitWorkerJob(std::bind(&GameDBParser::parseNoIntroDB, parser));
|
||||
m_db = db;
|
||||
return true;
|
||||
}
|
||||
@ -199,6 +190,77 @@ bool GBAApp::reloadGameDB() {
|
||||
}
|
||||
#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
|
||||
GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent)
|
||||
: QObject(parent)
|
||||
|
@ -8,9 +8,14 @@
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QMultiMap>
|
||||
#include <QObject>
|
||||
#include <QRunnable>
|
||||
#include <QString>
|
||||
#include <QThread>
|
||||
#include <QThreadPool>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "CoreManager.h"
|
||||
#include "MultiplayerController.h"
|
||||
@ -61,10 +66,34 @@ public:
|
||||
const NoIntroDB* gameDB() const { return m_db; }
|
||||
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:
|
||||
bool event(QEvent*);
|
||||
|
||||
private slots:
|
||||
void finishJob(qint64 jobId);
|
||||
|
||||
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();
|
||||
|
||||
void pauseAll(QList<Window*>* paused);
|
||||
@ -75,10 +104,12 @@ private:
|
||||
MultiplayerController m_multiplayer;
|
||||
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;
|
||||
#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)
|
||||
: QStackedWidget(parent)
|
||||
, m_config(config)
|
||||
@ -47,13 +37,13 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi
|
||||
|
||||
if (!path.isNull()) {
|
||||
// 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) {
|
||||
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);
|
||||
addWidget(m_libraryTree->widget());
|
||||
@ -61,25 +51,12 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi
|
||||
m_libraryGrid = new LibraryGrid(this);
|
||||
addWidget(m_libraryGrid->widget());
|
||||
|
||||
connect(&m_loaderThread, &QThread::finished, this, &LibraryController::refresh, Qt::QueuedConnection);
|
||||
|
||||
setViewStyle(LibraryStyle::STYLE_LIST);
|
||||
refresh();
|
||||
}
|
||||
|
||||
LibraryController::~LibraryController() {
|
||||
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) {
|
||||
@ -117,7 +94,7 @@ LibraryEntryRef LibraryController::selectedEntry() {
|
||||
VFile* LibraryController::selectedVFile() {
|
||||
LibraryEntryRef entry = selectedEntry();
|
||||
if (entry) {
|
||||
return mLibraryOpenVFile(m_library, entry->entry);
|
||||
return mLibraryOpenVFile(m_library.get(), entry->entry);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
@ -129,35 +106,26 @@ QPair<QString, QString> LibraryController::selectedPath() {
|
||||
}
|
||||
|
||||
void LibraryController::addDirectory(const QString& dir) {
|
||||
m_loaderThread.m_directory = dir;
|
||||
m_loaderThread.m_library = m_library;
|
||||
// The m_loaderThread temporarily owns the library
|
||||
m_library = nullptr;
|
||||
m_loaderThread.start();
|
||||
// The worker thread temporarily owns the library
|
||||
std::shared_ptr<mLibrary> library = m_library;
|
||||
m_libraryJob = GBAApp::app()->submitWorkerJob(std::bind(&LibraryController::loadDirectory, this, dir), this, [this, library]() {
|
||||
m_libraryJob = -1;
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
|
||||
void LibraryController::clear() {
|
||||
if (!m_library) {
|
||||
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
|
||||
m_library = m_loaderThread.m_library;
|
||||
m_loaderThread.m_library = nullptr;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (m_libraryJob > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLibraryClear(m_library);
|
||||
mLibraryClear(m_library.get());
|
||||
refresh();
|
||||
}
|
||||
|
||||
void LibraryController::refresh() {
|
||||
if (!m_library) {
|
||||
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
|
||||
m_library = m_loaderThread.m_library;
|
||||
m_loaderThread.m_library = nullptr;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (m_libraryJob > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDisabled(true);
|
||||
@ -166,7 +134,7 @@ void LibraryController::refresh() {
|
||||
QList<LibraryEntryRef> newEntries;
|
||||
|
||||
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++) {
|
||||
mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i);
|
||||
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 <QMap>
|
||||
#include <QThread>
|
||||
#include <QStackedWidget>
|
||||
|
||||
#include <mgba/core/library.h>
|
||||
@ -66,19 +65,6 @@ public:
|
||||
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 {
|
||||
Q_OBJECT
|
||||
|
||||
@ -110,9 +96,11 @@ private slots:
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
void loadDirectory(const QString&); // Called on separate thread
|
||||
|
||||
ConfigController* m_config = nullptr;
|
||||
LibraryLoaderThread m_loaderThread;
|
||||
mLibrary* m_library = nullptr;
|
||||
std::shared_ptr<mLibrary> m_library;
|
||||
qint64 m_libraryJob = -1;
|
||||
mLibraryListing m_listing;
|
||||
QMap<QString, LibraryEntryRef> m_entries;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user