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)