diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj
index d81106f512..71f500abf2 100644
--- a/Source/Core/Common/Common.vcxproj
+++ b/Source/Core/Common/Common.vcxproj
@@ -151,6 +151,7 @@
+
diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters
index a991205327..ceef3ae8e0 100644
--- a/Source/Core/Common/Common.vcxproj.filters
+++ b/Source/Core/Common/Common.vcxproj.filters
@@ -68,6 +68,7 @@
+
diff --git a/Source/Core/Common/WorkQueueThread.h b/Source/Core/Common/WorkQueueThread.h
new file mode 100644
index 0000000000..426d8b493d
--- /dev/null
+++ b/Source/Core/Common/WorkQueueThread.h
@@ -0,0 +1,86 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include
+
+#include "Common/Event.h"
+#include "Common/Flag.h"
+
+// A thread that executes the given function for every item placed into its queue.
+
+namespace Common
+{
+template
+class WorkQueueThread
+{
+public:
+ WorkQueueThread() = default;
+ WorkQueueThread(std::function function) { Reset(std::move(function)); }
+ ~WorkQueueThread() { Shutdown(); }
+ void Reset(std::function function)
+ {
+ Shutdown();
+ m_shutdown.Clear();
+ m_function = std::move(function);
+ m_thread = std::thread([this] { ThreadLoop(); });
+ }
+
+ template
+ void EmplaceItem(Args&&... args)
+ {
+ {
+ std::unique_lock lg(m_lock);
+ m_items.emplace(std::forward(args)...);
+ }
+ m_wakeup.Set();
+ }
+
+private:
+ void Shutdown()
+ {
+ if (m_thread.joinable())
+ {
+ m_shutdown.Set();
+ m_wakeup.Set();
+ m_thread.join();
+ }
+ }
+
+ void ThreadLoop()
+ {
+ while (true)
+ {
+ m_wakeup.Wait();
+
+ while (true)
+ {
+ T item;
+ {
+ std::unique_lock lg(m_lock);
+ if (m_items.empty())
+ break;
+ item = m_items.front();
+ m_items.pop();
+ }
+ m_function(std::move(item));
+ }
+
+ if (m_shutdown.IsSet())
+ break;
+ }
+ }
+
+ std::function m_function;
+ std::thread m_thread;
+ Common::Event m_wakeup;
+ Common::Flag m_shutdown;
+ std::mutex m_lock;
+ std::queue m_items;
+};
+
+} // namespace Common
diff --git a/Source/Core/DolphinQt2/GameList/GameTracker.cpp b/Source/Core/DolphinQt2/GameList/GameTracker.cpp
index 6050feaa8d..f1558f153d 100644
--- a/Source/Core/DolphinQt2/GameList/GameTracker.cpp
+++ b/Source/Core/DolphinQt2/GameList/GameTracker.cpp
@@ -17,23 +17,11 @@ static const QStringList game_filters{
GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
{
- m_loader = new GameLoader;
- m_loader->moveToThread(&m_loader_thread);
-
qRegisterMetaType>();
- connect(&m_loader_thread, &QThread::finished, m_loader, &QObject::deleteLater);
connect(this, &QFileSystemWatcher::directoryChanged, this, &GameTracker::UpdateDirectory);
connect(this, &QFileSystemWatcher::fileChanged, this, &GameTracker::UpdateFile);
- connect(this, &GameTracker::PathChanged, m_loader, &GameLoader::LoadGame);
- connect(m_loader, &GameLoader::GameLoaded, this, &GameTracker::GameLoaded);
- m_loader_thread.start();
-}
-
-GameTracker::~GameTracker()
-{
- m_loader_thread.quit();
- m_loader_thread.wait();
+ m_load_thread.Reset([this](const QString& path) { LoadGame(path); });
}
void GameTracker::AddDirectory(const QString& dir)
@@ -81,7 +69,7 @@ void GameTracker::UpdateDirectory(const QString& dir)
{
addPath(path);
m_tracked_files[path] = QSet{dir};
- emit PathChanged(path);
+ m_load_thread.EmplaceItem(path);
}
}
@@ -127,7 +115,7 @@ void GameTracker::UpdateFile(const QString& file)
GameRemoved(file);
addPath(file);
- emit PathChanged(file);
+ m_load_thread.EmplaceItem(file);
}
else if (removePath(file))
{
@@ -136,7 +124,7 @@ void GameTracker::UpdateFile(const QString& file)
}
}
-void GameLoader::LoadGame(const QString& path)
+void GameTracker::LoadGame(const QString& path)
{
if (!DiscIO::ShouldHideFromGameList(path.toStdString()))
{
diff --git a/Source/Core/DolphinQt2/GameList/GameTracker.h b/Source/Core/DolphinQt2/GameList/GameTracker.h
index 926e2a9ae2..550a6548ee 100644
--- a/Source/Core/DolphinQt2/GameList/GameTracker.h
+++ b/Source/Core/DolphinQt2/GameList/GameTracker.h
@@ -9,25 +9,19 @@
#include
#include
#include
-#include
-#include
+#include "Common/WorkQueueThread.h"
#include "DolphinQt2/GameList/GameFile.h"
-class GameLoader;
-
// Watches directories and loads GameFiles in a separate thread.
// To use this, just add directories using AddDirectory, and listen for the
-// GameLoaded and GameRemoved signals. Ignore the PathChanged signal, it's
-// only there because the Qt people made fileChanged and directoryChanged
-// private.
+// GameLoaded and GameRemoved signals.
class GameTracker final : public QFileSystemWatcher
{
Q_OBJECT
public:
explicit GameTracker(QObject* parent = nullptr);
- ~GameTracker();
void AddDirectory(const QString& dir);
void RemoveDirectory(const QString& dir);
@@ -36,28 +30,15 @@ signals:
void GameLoaded(QSharedPointer game);
void GameRemoved(const QString& path);
- void PathChanged(const QString& path);
-
private:
+ void LoadGame(const QString& path);
void UpdateDirectory(const QString& dir);
void UpdateFile(const QString& path);
QSet FindMissingFiles(const QString& dir);
// game path -> directories that track it
QMap> m_tracked_files;
- QThread m_loader_thread;
- GameLoader* m_loader;
-};
-
-class GameLoader final : public QObject
-{
- Q_OBJECT
-
-public:
- void LoadGame(const QString& path);
-
-signals:
- void GameLoaded(QSharedPointer game);
+ Common::WorkQueueThread m_load_thread;
};
Q_DECLARE_METATYPE(QSharedPointer)