From 3f8f4744c5354a6e3eccab1554634729c3c4b319 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 19 Oct 2025 20:50:29 -0500 Subject: [PATCH] Reduce CPU usage from background PC polling --- app/backend/computermanager.cpp | 20 ++++++++++++++------ app/backend/nvhttp.cpp | 33 +++++++++++++++++---------------- app/backend/nvhttp.h | 6 +++--- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/app/backend/computermanager.cpp b/app/backend/computermanager.cpp index 7f1991fc..9f5d1cac 100644 --- a/app/backend/computermanager.cpp +++ b/app/backend/computermanager.cpp @@ -29,9 +29,9 @@ public: } private: - bool tryPollComputer(NvAddress address, bool& changed) + bool tryPollComputer(QNetworkAccessManager* nam, NvAddress address, bool& changed) { - NvHTTP http(address, 0, m_Computer->serverCert); + NvHTTP http(address, 0, m_Computer->serverCert, nam); QString serverInfo; try { @@ -52,9 +52,9 @@ private: return true; } - bool updateAppList(bool& changed) + bool updateAppList(QNetworkAccessManager* nam, bool& changed) { - NvHTTP http(m_Computer); + NvHTTP http(m_Computer, nam); QVector appList; @@ -81,6 +81,14 @@ private: setServiceLevel(QThread::QualityOfService::Eco); #endif + // Share the QNetworkAccessManager to conserve resources when polling. + // Each instance creates a worker thread, so sharing them ensures that + // we are not spamming a new thread for every single polling attempt. + // + // Since QThread inherit the priority of the current thread, this also + // ensures that the NAM's worker thread will inherit our lower priority. + QNetworkAccessManager nam; + // Always fetch the applist the first time int pollsSinceLastAppListFetch = POLLS_PER_APPLIST_FETCH; while (!isInterruptionRequested()) { @@ -93,7 +101,7 @@ private: return; } - if (tryPollComputer(address, stateChanged)) { + if (tryPollComputer(&nam, address, stateChanged)) { if (!wasOnline) { qInfo() << m_Computer->name << "is now online at" << m_Computer->activeAddress.toString(); } @@ -124,7 +132,7 @@ private: stateChanged = false; } - if (updateAppList(stateChanged)) { + if (updateAppList(&nam, stateChanged)) { pollsSinceLastAppListFetch = 0; } } diff --git a/app/backend/nvhttp.cpp b/app/backend/nvhttp.cpp index bbbe8540..1d7d706b 100644 --- a/app/backend/nvhttp.cpp +++ b/app/backend/nvhttp.cpp @@ -18,7 +18,8 @@ #define RESUME_TIMEOUT_MS 30000 #define QUIT_TIMEOUT_MS 30000 -NvHTTP::NvHTTP(NvAddress address, uint16_t httpsPort, QSslCertificate serverCert) : +NvHTTP::NvHTTP(NvAddress address, uint16_t httpsPort, QSslCertificate serverCert, QNetworkAccessManager* nam) : + m_Nam(nam ? nam : new QNetworkAccessManager(this)), m_ServerCert(serverCert) { m_BaseUrlHttp.setScheme("http"); @@ -29,13 +30,11 @@ NvHTTP::NvHTTP(NvAddress address, uint16_t httpsPort, QSslCertificate serverCert // Never use a proxy server QNetworkProxy noProxy(QNetworkProxy::NoProxy); - m_Nam.setProxy(noProxy); - - connect(&m_Nam, &QNetworkAccessManager::sslErrors, this, &NvHTTP::handleSslErrors); + m_Nam->setProxy(noProxy); } -NvHTTP::NvHTTP(NvComputer* computer) : - NvHTTP(computer->activeAddress, computer->activeHttpsPort, computer->serverCert) +NvHTTP::NvHTTP(NvComputer* computer, QNetworkAccessManager* nam) : + NvHTTP(computer->activeAddress, computer->activeHttpsPort, computer->serverCert, nam) { } @@ -492,15 +491,15 @@ NvHTTP::openConnection(QUrl baseUrl, request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false); #endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && QT_VERSION < QT_VERSION_CHECK(5, 15, 1) && !defined(QT_NO_BEARERMANAGEMENT) - // HACK: Set network accessibility to work around QTBUG-80947 (introduced in Qt 5.14.0 and fixed in Qt 5.15.1) - QT_WARNING_PUSH - QT_WARNING_DISABLE_DEPRECATED - m_Nam.setNetworkAccessible(QNetworkAccessManager::Accessible); - QT_WARNING_POP +#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) + // Use fine-grained idle timeouts to avoid calling QNetworkAccessManager::clearAccessCache(), + // which tears down the NAM's global thread each time. We must not keep persistent connections + // or GFE will puke. + request.setAttribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute, 0); #endif - QNetworkReply* reply = m_Nam.get(request); + auto sslErrorsConnection = connect(m_Nam, &QNetworkAccessManager::sslErrors, this, &NvHTTP::handleSslErrors); + QNetworkReply* reply = m_Nam->get(request); // Run the request with a timeout if requested QEventLoop loop; @@ -523,9 +522,11 @@ NvHTTP::openConnection(QUrl baseUrl, reply->abort(); } - // We must clear out cached authentication and connections or - // GFE will puke next time - m_Nam.clearAccessCache(); +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) + // If we couldn't use fine-grained connection idle timeouts, kill them all now + m_Nam->clearAccessCache(); +#endif + disconnect(sslErrorsConnection); // Handle error if (reply->error() != QNetworkReply::NoError) diff --git a/app/backend/nvhttp.h b/app/backend/nvhttp.h index 6e8be54c..27caa441 100644 --- a/app/backend/nvhttp.h +++ b/app/backend/nvhttp.h @@ -109,9 +109,9 @@ public: NVLL_VERBOSE }; - explicit NvHTTP(NvAddress address, uint16_t httpsPort, QSslCertificate serverCert); + explicit NvHTTP(NvAddress address, uint16_t httpsPort, QSslCertificate serverCert, QNetworkAccessManager* nam = nullptr); - explicit NvHTTP(NvComputer* computer); + explicit NvHTTP(NvComputer* computer, QNetworkAccessManager* nam = nullptr); static int @@ -196,6 +196,6 @@ private: NvLogLevel logLevel); NvAddress m_Address; - QNetworkAccessManager m_Nam; + QNetworkAccessManager* m_Nam; QSslCertificate m_ServerCert; };