From de9378bf6389ad22ca955df477a48bd678c10c9e Mon Sep 17 00:00:00 2001 From: Michael M Date: Sun, 20 Aug 2017 14:04:49 -0700 Subject: [PATCH 1/2] Common: add WorkQueueThread --- Source/Core/Common/Common.vcxproj | 1 + Source/Core/Common/Common.vcxproj.filters | 1 + Source/Core/Common/WorkQueueThread.h | 86 +++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 Source/Core/Common/WorkQueueThread.h diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 651808e5f7..493e5ab626 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -152,6 +152,7 @@ + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index c7ceb7adcb..62abbcc886 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..8947b099cb --- /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::move(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 From 8c13e0230ced0dbc2135e48e332ab79a9c4b3567 Mon Sep 17 00:00:00 2001 From: Michael M Date: Sun, 20 Aug 2017 14:06:33 -0700 Subject: [PATCH 2/2] GameTracker: use WorkQueueThread --- Source/Core/Common/WorkQueueThread.h | 2 +- .../Core/DolphinQt2/GameList/GameTracker.cpp | 20 +++----------- Source/Core/DolphinQt2/GameList/GameTracker.h | 27 +++---------------- 3 files changed, 9 insertions(+), 40 deletions(-) diff --git a/Source/Core/Common/WorkQueueThread.h b/Source/Core/Common/WorkQueueThread.h index 8947b099cb..426d8b493d 100644 --- a/Source/Core/Common/WorkQueueThread.h +++ b/Source/Core/Common/WorkQueueThread.h @@ -35,7 +35,7 @@ public: { { std::unique_lock lg(m_lock); - m_items.emplace(std::move(args)...); + m_items.emplace(std::forward(args)...); } m_wakeup.Set(); } 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)