Only reserialize hosts if a serializable attribute changed

This commit is contained in:
Cameron Gutman 2023-10-01 17:28:06 -05:00
parent facd6e4e56
commit 0976bc39b3
4 changed files with 64 additions and 9 deletions

View file

@ -167,6 +167,7 @@ ComputerManager::ComputerManager(QObject *parent)
settings.setArrayIndex(i);
NvComputer* computer = new NvComputer(settings);
m_KnownHosts[computer->uuid] = computer;
m_LastSerializedHosts[computer->uuid] = *computer;
}
settings.endArray();
@ -250,6 +251,14 @@ void DelayedFlushThread::run() {
// Reset the delayed flush flag to ensure any racing saveHosts() call will set it again
m_ComputerManager->m_NeedsDelayedFlush = false;
// Update the last serialized hosts map under the delayed flush mutex
m_ComputerManager->m_LastSerializedHosts.clear();
for (const NvComputer* computer : m_ComputerManager->m_KnownHosts) {
// Copy the current state of the NvComputer to allow us to check later if we need
// to serialize it again when attribute updates occur.
m_ComputerManager->m_LastSerializedHosts[computer->uuid] = *computer;
}
}
// Perform the flush
@ -433,6 +442,17 @@ void ComputerManager::handleMdnsServiceResolved(MdnsPendingComputer* computer,
computer->deleteLater();
}
void ComputerManager::saveHost(NvComputer *computer)
{
// If no serializable properties changed, don't bother saving hosts
QMutexLocker lock(&m_DelayedFlushMutex);
if (!m_LastSerializedHosts.value(computer->uuid).isEqualSerialized(*computer)) {
// Queue a request for a delayed flush to QSettings outside of the lock
lock.unlock();
saveHosts();
}
}
void ComputerManager::handleComputerStateChanged(NvComputer* computer)
{
emit computerStateChanged(computer);
@ -442,8 +462,8 @@ void ComputerManager::handleComputerStateChanged(NvComputer* computer)
emit quitAppCompleted(QVariant());
}
// Save updated hosts to QSettings
saveHosts();
// Save updates to this host
saveHost(computer);
}
QVector<NvComputer*> ComputerManager::getComputers()
@ -479,7 +499,7 @@ public:
m_ComputerManager->m_KnownHosts.remove(m_Computer->uuid);
}
// Persist the new host list
// Persist the new host list with this computer deleted
m_ComputerManager->saveHosts();
// Delete the polling entry first. This will stop all polling threads too.
@ -520,9 +540,6 @@ void ComputerManager::renameHost(NvComputer* computer, QString name)
void ComputerManager::clientSideAttributeUpdated(NvComputer* computer)
{
// Persist the change
saveHosts();
// Notify the UI of the state change
handleComputerStateChanged(computer);
}
@ -580,7 +597,7 @@ private:
break;
case NvPairingManager::PairState::PAIRED:
// Persist the newly pinned server certificate for this host
m_ComputerManager->saveHosts();
m_ComputerManager->saveHost(m_Computer);
emit pairingCompleted(m_Computer, nullptr);
break;

View file

@ -251,6 +251,8 @@ private slots:
private:
void saveHosts();
void saveHost(NvComputer* computer);
QHostAddress getBestGlobalAddressV6(QVector<QHostAddress>& addresses);
void startPollingComputer(NvComputer* computer);
@ -259,6 +261,7 @@ private:
QReadWriteLock m_Lock;
QMap<QString, NvComputer*> m_KnownHosts;
QMap<QString, ComputerPollingEntry*> m_PollEntries;
QHash<QString, NvComputer> m_LastSerializedHosts; // Protected by m_DelayedFlushMutex
QSharedPointer<QMdnsEngine::Server> m_MdnsServer;
QMdnsEngine::Browser* m_MdnsBrowser;
QVector<MdnsPendingComputer*> m_PendingResolution;

View file

@ -94,7 +94,7 @@ void NvComputer::serialize(QSettings& settings, bool serializeApps) const
settings.setValue(SER_NVIDIASOFTWARE, isNvidiaServerSoftware);
// Avoid deleting an existing applist if we couldn't get one
if (!appList.isEmpty()) {
if (!appList.isEmpty() && serializeApps) {
settings.remove(SER_APPLIST);
settings.beginWriteArray(SER_APPLIST);
for (int i = 0; i < appList.count(); i++) {
@ -105,6 +105,21 @@ void NvComputer::serialize(QSettings& settings, bool serializeApps) const
}
}
bool NvComputer::isEqualSerialized(const NvComputer &that) const
{
return this->name == that.name &&
this->hasCustomName == that.hasCustomName &&
this->uuid == that.uuid &&
this->macAddress == that.macAddress &&
this->localAddress == that.localAddress &&
this->remoteAddress == that.remoteAddress &&
this->ipv6Address == that.ipv6Address &&
this->manualAddress == that.manualAddress &&
this->serverCert == that.serverCert &&
this->isNvidiaServerSoftware == that.isNvidiaServerSoftware &&
this->appList == that.appList;
}
void NvComputer::sortAppList()
{
std::stable_sort(appList.begin(), appList.end(), [](const NvApp& app1, const NvApp& app2) {

View file

@ -8,6 +8,16 @@
#include <QSettings>
#include <QRunnable>
class CopySafeReadWriteLock : public QReadWriteLock
{
public:
CopySafeReadWriteLock() = default;
// Don't actually copy the QReadWriteLock
CopySafeReadWriteLock(const CopySafeReadWriteLock&) {}
CopySafeReadWriteLock& operator=(const CopySafeReadWriteLock &) { return *this; }
};
class NvComputer
{
friend class PcMonitorThread;
@ -22,6 +32,12 @@ private:
bool pendingQuit;
public:
NvComputer() = default;
NvComputer(const NvComputer& other) = default;
NvComputer& operator=(const NvComputer &) = default;
explicit NvComputer(NvHTTP& http, QString serverInfo);
explicit NvComputer(QSettings& settings);
@ -51,6 +67,9 @@ public:
void
serialize(QSettings& settings, bool serializeApps) const;
bool
isEqualSerialized(const NvComputer& that) const;
enum PairState
{
PS_UNKNOWN,
@ -91,9 +110,10 @@ public:
QSslCertificate serverCert;
QVector<NvApp> appList;
bool isNvidiaServerSoftware;
// Remember to update isEqualSerialized() when adding fields here!
// Synchronization
mutable QReadWriteLock lock;
mutable CopySafeReadWriteLock lock;
private:
uint16_t externalPort;