mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-12-17 14:43:09 +00:00
Use a single flush thread for ComputerManager
Some QSettings backends don't like concurrent writes and using a single thread improves write batching behavior.
This commit is contained in:
parent
59cae610d2
commit
16a9ed682e
2 changed files with 67 additions and 21 deletions
|
@ -148,7 +148,7 @@ ComputerManager::ComputerManager(QObject *parent)
|
||||||
m_PollingRef(0),
|
m_PollingRef(0),
|
||||||
m_MdnsBrowser(nullptr),
|
m_MdnsBrowser(nullptr),
|
||||||
m_CompatFetcher(nullptr),
|
m_CompatFetcher(nullptr),
|
||||||
m_HostsListDirty(false)
|
m_NeedsDelayedFlush(false)
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
|
||||||
|
@ -164,6 +164,10 @@ ComputerManager::ComputerManager(QObject *parent)
|
||||||
// Fetch latest compatibility data asynchronously
|
// Fetch latest compatibility data asynchronously
|
||||||
m_CompatFetcher.start();
|
m_CompatFetcher.start();
|
||||||
|
|
||||||
|
// Start the delayed flush thread to handle saveHosts() calls
|
||||||
|
m_DelayedFlushThread = new DelayedFlushThread(this);
|
||||||
|
m_DelayedFlushThread->start();
|
||||||
|
|
||||||
// To quit in a timely manner, we must block additional requests
|
// To quit in a timely manner, we must block additional requests
|
||||||
// after we receive the aboutToQuit() signal. This is neccessary
|
// after we receive the aboutToQuit() signal. This is neccessary
|
||||||
// because NvHTTP uses aboutToQuit() to abort requests in progres
|
// because NvHTTP uses aboutToQuit() to abort requests in progres
|
||||||
|
@ -174,6 +178,18 @@ ComputerManager::ComputerManager(QObject *parent)
|
||||||
|
|
||||||
ComputerManager::~ComputerManager()
|
ComputerManager::~ComputerManager()
|
||||||
{
|
{
|
||||||
|
// Stop the delayed flush thread before acquiring the lock in write mode
|
||||||
|
// to avoid deadlocking with a flush that needs the lock in read mode.
|
||||||
|
{
|
||||||
|
// Wake the delayed flush thread
|
||||||
|
m_DelayedFlushThread->requestInterruption();
|
||||||
|
m_DelayedFlushCondition.wakeOne();
|
||||||
|
|
||||||
|
// Wait for it to terminate (and finish any pending flush)
|
||||||
|
m_DelayedFlushThread->wait();
|
||||||
|
delete m_DelayedFlushThread;
|
||||||
|
}
|
||||||
|
|
||||||
QWriteLocker lock(&m_Lock);
|
QWriteLocker lock(&m_Lock);
|
||||||
|
|
||||||
// Delete machines that haven't been resolved yet
|
// Delete machines that haven't been resolved yet
|
||||||
|
@ -203,19 +219,27 @@ ComputerManager::~ComputerManager()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeferredHostSaveTask : public QRunnable
|
void DelayedFlushThread::run() {
|
||||||
{
|
while (!QThread::currentThread()->isInterruptionRequested()) {
|
||||||
public:
|
// Wait for a delayed flush request or an interruption
|
||||||
DeferredHostSaveTask(ComputerManager* cm)
|
{
|
||||||
: m_ComputerManager(cm) {}
|
QMutexLocker locker(&m_ComputerManager->m_DelayedFlushMutex);
|
||||||
|
|
||||||
void run()
|
while (!QThread::currentThread()->isInterruptionRequested() && !m_ComputerManager->m_NeedsDelayedFlush) {
|
||||||
{
|
m_ComputerManager->m_DelayedFlushCondition.wait(&m_ComputerManager->m_DelayedFlushMutex);
|
||||||
// Only persist the host list if it was dirty.
|
}
|
||||||
//
|
|
||||||
// NB: We can have multiple save tasks queued up and so another one could
|
// Reset the delayed flush flag to ensure any racing saveHosts() call will set it again
|
||||||
// have persisted the changes already.
|
m_ComputerManager->m_NeedsDelayedFlush = false;
|
||||||
if (m_ComputerManager->m_HostsListDirty.testAndSetAcquire(true, false)) {
|
}
|
||||||
|
|
||||||
|
if (QThread::currentThread()->isInterruptionRequested()) {
|
||||||
|
// Bail without flushing if we woke up for an interruption
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the flush
|
||||||
|
{
|
||||||
QReadLocker lock(&m_ComputerManager->m_Lock);
|
QReadLocker lock(&m_ComputerManager->m_Lock);
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
@ -229,17 +253,17 @@ public:
|
||||||
settings.endArray();
|
settings.endArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private:
|
|
||||||
ComputerManager* m_ComputerManager;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ComputerManager::saveHosts()
|
void ComputerManager::saveHosts()
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(m_DelayedFlushThread != nullptr && m_DelayedFlushThread->isRunning());
|
||||||
|
|
||||||
// Punt to a worker thread because QSettings on macOS can take ages (> 500 ms)
|
// Punt to a worker thread because QSettings on macOS can take ages (> 500 ms)
|
||||||
// to persist our host list to disk (especially when a host has a bunch of apps).
|
// to persist our host list to disk (especially when a host has a bunch of apps).
|
||||||
m_HostsListDirty.storeRelease(true);
|
QMutexLocker locker(&m_DelayedFlushMutex);
|
||||||
QThreadPool::globalInstance()->start(new DeferredHostSaveTask(this));
|
m_NeedsDelayedFlush = true;
|
||||||
|
m_DelayedFlushCondition.wakeOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
QHostAddress ComputerManager::getBestGlobalAddressV6(QVector<QHostAddress> &addresses)
|
QHostAddress ComputerManager::getBestGlobalAddressV6(QVector<QHostAddress> &addresses)
|
||||||
|
|
|
@ -15,6 +15,25 @@
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
|
||||||
|
class DelayedFlushThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DelayedFlushThread(ComputerManager* cm)
|
||||||
|
: m_ComputerManager(cm)
|
||||||
|
{
|
||||||
|
setObjectName("CM Delayed Flush Thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComputerManager* m_ComputerManager;
|
||||||
|
};
|
||||||
|
|
||||||
class MdnsPendingComputer : public QObject
|
class MdnsPendingComputer : public QObject
|
||||||
{
|
{
|
||||||
|
@ -167,7 +186,7 @@ class ComputerManager : public QObject
|
||||||
friend class DeferredHostDeletionTask;
|
friend class DeferredHostDeletionTask;
|
||||||
friend class PendingAddTask;
|
friend class PendingAddTask;
|
||||||
friend class PendingPairingTask;
|
friend class PendingPairingTask;
|
||||||
friend class DeferredHostSaveTask;
|
friend class DelayedFlushThread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ComputerManager(QObject *parent = nullptr);
|
explicit ComputerManager(QObject *parent = nullptr);
|
||||||
|
@ -229,5 +248,8 @@ private:
|
||||||
QMdnsEngine::Cache m_MdnsCache;
|
QMdnsEngine::Cache m_MdnsCache;
|
||||||
QVector<MdnsPendingComputer*> m_PendingResolution;
|
QVector<MdnsPendingComputer*> m_PendingResolution;
|
||||||
CompatFetcher m_CompatFetcher;
|
CompatFetcher m_CompatFetcher;
|
||||||
QAtomicInteger<bool> m_HostsListDirty;
|
DelayedFlushThread* m_DelayedFlushThread;
|
||||||
|
QMutex m_DelayedFlushMutex;
|
||||||
|
QWaitCondition m_DelayedFlushCondition;
|
||||||
|
bool m_NeedsDelayedFlush;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue