Skip to content
Permalink
Browse files
Merge pull request #5951 from ligfx/gametrackerworkqueuethread
GameTracker: use new Common::WorkQueueThread instead of signals/slots
  • Loading branch information
Helios747 committed Aug 23, 2017
2 parents 4170231 + 8c13e02 commit 935c1da
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 39 deletions.
@@ -151,6 +151,7 @@
<ClInclude Include="TraversalClient.h" />
<ClInclude Include="TraversalProto.h" />
<ClInclude Include="UPnP.h" />
<ClInclude Include="WorkQueueThread.h" />
<ClInclude Include="x64ABI.h" />
<ClInclude Include="x64Emitter.h" />
<ClInclude Include="x64Reg.h" />
@@ -68,6 +68,7 @@
<ClInclude Include="SysConf.h" />
<ClInclude Include="Thread.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="WorkQueueThread.h" />
<ClInclude Include="x64ABI.h" />
<ClInclude Include="x64Emitter.h" />
<ClInclude Include="x64Reg.h" />
@@ -0,0 +1,86 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <functional>
#include <queue>
#include <thread>

#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 <typename T>
class WorkQueueThread
{
public:
WorkQueueThread() = default;
WorkQueueThread(std::function<void(T)> function) { Reset(std::move(function)); }
~WorkQueueThread() { Shutdown(); }
void Reset(std::function<void(T)> function)
{
Shutdown();
m_shutdown.Clear();
m_function = std::move(function);
m_thread = std::thread([this] { ThreadLoop(); });
}

template <typename... Args>
void EmplaceItem(Args&&... args)
{
{
std::unique_lock<std::mutex> lg(m_lock);
m_items.emplace(std::forward<Args>(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<std::mutex> 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<void(T)> m_function;
std::thread m_thread;
Common::Event m_wakeup;
Common::Flag m_shutdown;
std::mutex m_lock;
std::queue<T> m_items;
};

} // namespace Common
@@ -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<QSharedPointer<GameFile>>();
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<QString>{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()))
{
@@ -9,25 +9,19 @@
#include <QSet>
#include <QSharedPointer>
#include <QString>
#include <QStringList>
#include <QThread>

#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 @@ class GameTracker final : public QFileSystemWatcher
void GameLoaded(QSharedPointer<GameFile> 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<QString> FindMissingFiles(const QString& dir);

// game path -> directories that track it
QMap<QString, QSet<QString>> 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<GameFile> game);
Common::WorkQueueThread<QString> m_load_thread;
};

Q_DECLARE_METATYPE(QSharedPointer<GameFile>)

0 comments on commit 935c1da

Please sign in to comment.