diff --git a/src/app/application.cpp b/src/app/application.cpp index 5b32738f2..45e6dc43a 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -129,6 +129,9 @@ Application::Application(int &argc, char **argv) , m_storeFileLoggerAgeType(FILELOGGER_SETTINGS_KEY(u"AgeType"_qs)) , m_storeFileLoggerPath(FILELOGGER_SETTINGS_KEY(u"Path"_qs)) , m_storeMemoryWorkingSetLimit(SETTINGS_KEY(u"MemoryWorkingSetLimit"_qs)) +#ifdef Q_OS_WIN + , m_processMemoryPriority(SETTINGS_KEY(u"ProcessMemoryPriority"_qs)) +#endif { qRegisterMetaType("Log::Msg"); qRegisterMetaType("Log::Peer"); @@ -607,6 +610,7 @@ int Application::exec(const QStringList ¶ms) #endif #ifdef Q_OS_WIN + applyMemoryPriority(); adjustThreadPriority(); #endif @@ -822,6 +826,75 @@ void Application::applyMemoryWorkingSetLimit() const #endif #ifdef Q_OS_WIN +MemoryPriority Application::processMemoryPriority() const +{ + return m_processMemoryPriority.get(MemoryPriority::BelowNormal); +} + +void Application::setProcessMemoryPriority(const MemoryPriority priority) +{ + if (processMemoryPriority() == priority) + return; + + m_processMemoryPriority = priority; + applyMemoryPriority(); +} + +void Application::applyMemoryPriority() const +{ + using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD); + const auto setProcessInformation = Utils::Misc::loadWinAPI(u"Kernel32.dll"_qs, "SetProcessInformation"); + if (!setProcessInformation) // only available on Windows >= 8 + return; + + using SETTHREADINFORMATION = BOOL (WINAPI *)(HANDLE, THREAD_INFORMATION_CLASS, LPVOID, DWORD); + const auto setThreadInformation = Utils::Misc::loadWinAPI(u"Kernel32.dll"_qs, "SetThreadInformation"); + if (!setThreadInformation) // only available on Windows >= 8 + return; + +#if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + // this dummy struct is required to compile successfully when targeting older Windows version + struct MEMORY_PRIORITY_INFORMATION + { + ULONG MemoryPriority; + }; + +#define MEMORY_PRIORITY_LOWEST 0 +#define MEMORY_PRIORITY_VERY_LOW 1 +#define MEMORY_PRIORITY_LOW 2 +#define MEMORY_PRIORITY_MEDIUM 3 +#define MEMORY_PRIORITY_BELOW_NORMAL 4 +#define MEMORY_PRIORITY_NORMAL 5 +#endif + + MEMORY_PRIORITY_INFORMATION prioInfo {}; + switch (processMemoryPriority()) + { + case MemoryPriority::Normal: + default: + prioInfo.MemoryPriority = MEMORY_PRIORITY_NORMAL; + break; + case MemoryPriority::BelowNormal: + prioInfo.MemoryPriority = MEMORY_PRIORITY_BELOW_NORMAL; + break; + case MemoryPriority::Medium: + prioInfo.MemoryPriority = MEMORY_PRIORITY_MEDIUM; + break; + case MemoryPriority::Low: + prioInfo.MemoryPriority = MEMORY_PRIORITY_LOW; + break; + case MemoryPriority::VeryLow: + prioInfo.MemoryPriority = MEMORY_PRIORITY_VERY_LOW; + break; + } + setProcessInformation(::GetCurrentProcess(), ProcessMemoryPriority, &prioInfo, sizeof(prioInfo)); + + // To avoid thrashing/sluggishness of the app, set "main event loop" thread to normal memory priority + // which is higher/equal than other threads + prioInfo.MemoryPriority = MEMORY_PRIORITY_NORMAL; + setThreadInformation(::GetCurrentThread(), ThreadMemoryPriority, &prioInfo, sizeof(prioInfo)); +} + void Application::adjustThreadPriority() const { // Workaround for improving responsiveness of qbt when CPU resources are scarce. diff --git a/src/app/application.h b/src/app/application.h index bf6ba52c1..162cab62d 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -118,6 +118,11 @@ public: int memoryWorkingSetLimit() const override; void setMemoryWorkingSetLimit(int size) override; +#ifdef Q_OS_WIN + MemoryPriority processMemoryPriority() const override; + void setProcessMemoryPriority(MemoryPriority priority) override; +#endif + #ifndef DISABLE_GUI MainWindow *mainWindow() override; #endif @@ -151,6 +156,7 @@ private: #endif #ifdef Q_OS_WIN + void applyMemoryPriority() const; void adjustThreadPriority() const; #endif @@ -183,6 +189,10 @@ private: SettingValue m_storeFileLoggerPath; SettingValue m_storeMemoryWorkingSetLimit; +#ifdef Q_OS_WIN + SettingValue m_processMemoryPriority; +#endif + #ifndef DISABLE_GUI MainWindow *m_window = nullptr; #endif diff --git a/src/app/upgrade.cpp b/src/app/upgrade.cpp index 1121f43be..9a22515c5 100644 --- a/src/app/upgrade.cpp +++ b/src/app/upgrade.cpp @@ -28,6 +28,7 @@ #include "upgrade.h" +#include #include #include "base/bittorrent/torrentcontentlayout.h" @@ -44,7 +45,7 @@ namespace { - const int MIGRATION_VERSION = 3; + const int MIGRATION_VERSION = 4; const QString MIGRATION_VERSION_KEY = u"Meta/MigrationVersion"_qs; void exportWebUIHttpsFiles() @@ -368,6 +369,21 @@ namespace } } } + +#ifdef Q_OS_WIN + void migrateMemoryPrioritySettings() + { + auto *settingsStorage = SettingsStorage::instance(); + const QString oldKey = u"BitTorrent/OSMemoryPriority"_qs; + const QString newKey = u"Application/ProcessMemoryPriority"_qs; + + if (settingsStorage->hasKey(oldKey)) + { + const auto value = settingsStorage->loadValue(oldKey); + settingsStorage->storeValue(newKey, value); + } + } +#endif } bool upgrade(const bool /*ask*/) @@ -392,6 +408,11 @@ bool upgrade(const bool /*ask*/) if (version < 3) migrateProxySettingsEnum(); +#ifdef Q_OS_WIN + if (version < 4) + migrateMemoryPrioritySettings(); +#endif + version = MIGRATION_VERSION; } diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index fda39bfcd..5310bf6db 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -447,9 +447,6 @@ Session::Session(QObject *parent) } ) , m_resumeDataStorageType(BITTORRENT_SESSION_KEY(u"ResumeDataStorageType"_qs), ResumeDataStorageType::Legacy) -#if defined(Q_OS_WIN) - , m_OSMemoryPriority(BITTORRENT_KEY(u"OSMemoryPriority"_qs), OSMemoryPriority::BelowNormal) -#endif , m_seedingLimitTimer {new QTimer {this}} , m_resumeDataTimer {new QTimer {this}} , m_statistics {new Statistics {this}} @@ -1067,10 +1064,6 @@ void Session::configureComponents() disableIPFilter(); m_IPFilteringConfigured = true; } - -#if defined(Q_OS_WIN) - applyOSMemoryPriority(); -#endif } void Session::prepareStartup() @@ -3512,67 +3505,6 @@ bool Session::isRestored() const return m_isRestored; } -#if defined(Q_OS_WIN) -OSMemoryPriority Session::getOSMemoryPriority() const -{ - return m_OSMemoryPriority; -} - -void Session::setOSMemoryPriority(const OSMemoryPriority priority) -{ - if (m_OSMemoryPriority == priority) - return; - - m_OSMemoryPriority = priority; - configureDeferred(); -} - -void Session::applyOSMemoryPriority() const -{ - using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD); - const auto setProcessInformation = Utils::Misc::loadWinAPI(u"Kernel32.dll"_qs, "SetProcessInformation"); - if (!setProcessInformation) // only available on Windows >= 8 - return; - -#if (_WIN32_WINNT < _WIN32_WINNT_WIN8) - // this dummy struct is required to compile successfully when targeting older Windows version - struct MEMORY_PRIORITY_INFORMATION - { - ULONG MemoryPriority; - }; - -#define MEMORY_PRIORITY_LOWEST 0 -#define MEMORY_PRIORITY_VERY_LOW 1 -#define MEMORY_PRIORITY_LOW 2 -#define MEMORY_PRIORITY_MEDIUM 3 -#define MEMORY_PRIORITY_BELOW_NORMAL 4 -#define MEMORY_PRIORITY_NORMAL 5 -#endif - - MEMORY_PRIORITY_INFORMATION prioInfo {}; - switch (getOSMemoryPriority()) - { - case OSMemoryPriority::Normal: - default: - prioInfo.MemoryPriority = MEMORY_PRIORITY_NORMAL; - break; - case OSMemoryPriority::BelowNormal: - prioInfo.MemoryPriority = MEMORY_PRIORITY_BELOW_NORMAL; - break; - case OSMemoryPriority::Medium: - prioInfo.MemoryPriority = MEMORY_PRIORITY_MEDIUM; - break; - case OSMemoryPriority::Low: - prioInfo.MemoryPriority = MEMORY_PRIORITY_LOW; - break; - case OSMemoryPriority::VeryLow: - prioInfo.MemoryPriority = MEMORY_PRIORITY_VERY_LOW; - break; - } - setProcessInformation(::GetCurrentProcess(), ProcessMemoryPriority, &prioInfo, sizeof(prioInfo)); -} -#endif - int Session::maxConnectionsPerTorrent() const { return m_maxConnectionsPerTorrent; diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 6b588504d..dd2ff6c62 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -29,7 +29,6 @@ #pragma once -#include #include #include @@ -151,18 +150,6 @@ namespace BitTorrent SQLite }; Q_ENUM_NS(ResumeDataStorageType) - -#if defined(Q_OS_WIN) - enum class OSMemoryPriority : int - { - Normal = 0, - BelowNormal = 1, - Medium = 2, - Low = 3, - VeryLow = 4 - }; - Q_ENUM_NS(OSMemoryPriority) -#endif } struct SessionMetricIndices @@ -464,10 +451,6 @@ namespace BitTorrent void setBannedIPs(const QStringList &newList); ResumeDataStorageType resumeDataStorageType() const; void setResumeDataStorageType(ResumeDataStorageType type); -#if defined(Q_OS_WIN) - OSMemoryPriority getOSMemoryPriority() const; - void setOSMemoryPriority(OSMemoryPriority priority); -#endif bool isRestored() const; @@ -629,9 +612,6 @@ namespace BitTorrent void populateAdditionalTrackers(); void enableIPFilter(); void disableIPFilter(); -#if defined(Q_OS_WIN) - void applyOSMemoryPriority() const; -#endif void processTrackerStatuses(); void populateExcludedFileNamesRegExpList(); void prepareStartup(); @@ -795,9 +775,6 @@ namespace BitTorrent CachedSettingValue m_excludedFileNames; CachedSettingValue m_bannedIPs; CachedSettingValue m_resumeDataStorageType; -#if defined(Q_OS_WIN) - CachedSettingValue m_OSMemoryPriority; -#endif bool m_isRestored = false; diff --git a/src/base/interfaces/iapplication.h b/src/base/interfaces/iapplication.h index 94cdbdb43..3942fd9d0 100644 --- a/src/base/interfaces/iapplication.h +++ b/src/base/interfaces/iapplication.h @@ -30,11 +30,31 @@ #pragma once +#include +#include + class QString; class Path; struct QBtCommandLineParameters; +#ifdef Q_OS_WIN +inline namespace ApplicationSettingsEnums +{ + Q_NAMESPACE + + enum class MemoryPriority : int + { + Normal = 0, + BelowNormal = 1, + Medium = 2, + Low = 3, + VeryLow = 4 + }; + Q_ENUM_NS(MemoryPriority) +} +#endif + class IApplication { public: @@ -58,4 +78,9 @@ public: virtual int memoryWorkingSetLimit() const = 0; virtual void setMemoryWorkingSetLimit(int size) = 0; + +#ifdef Q_OS_WIN + virtual MemoryPriority processMemoryPriority() const = 0; + virtual void setProcessMemoryPriority(MemoryPriority priority) = 0; +#endif }; diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index 64cfd1009..4a1201c6b 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -185,7 +185,7 @@ void AdvancedSettings::saveAdvancedSettings() const app()->setMemoryWorkingSetLimit(m_spinBoxMemoryWorkingSetLimit.value()); #endif #if defined(Q_OS_WIN) - session->setOSMemoryPriority(m_comboBoxOSMemoryPriority.currentData().value()); + app()->setProcessMemoryPriority(m_comboBoxOSMemoryPriority.currentData().value()); #endif // Async IO threads session->setAsyncIOThreads(m_spinBoxAsyncIOThreads.value()); @@ -422,12 +422,12 @@ void AdvancedSettings::loadAdvancedSettings() , &m_spinBoxMemoryWorkingSetLimit); #endif #if defined(Q_OS_WIN) - m_comboBoxOSMemoryPriority.addItem(tr("Normal"), QVariant::fromValue(BitTorrent::OSMemoryPriority::Normal)); - m_comboBoxOSMemoryPriority.addItem(tr("Below normal"), QVariant::fromValue(BitTorrent::OSMemoryPriority::BelowNormal)); - m_comboBoxOSMemoryPriority.addItem(tr("Medium"), QVariant::fromValue(BitTorrent::OSMemoryPriority::Medium)); - m_comboBoxOSMemoryPriority.addItem(tr("Low"), QVariant::fromValue(BitTorrent::OSMemoryPriority::Low)); - m_comboBoxOSMemoryPriority.addItem(tr("Very low"), QVariant::fromValue(BitTorrent::OSMemoryPriority::VeryLow)); - m_comboBoxOSMemoryPriority.setCurrentIndex(m_comboBoxOSMemoryPriority.findData(QVariant::fromValue(session->getOSMemoryPriority()))); + m_comboBoxOSMemoryPriority.addItem(tr("Normal"), QVariant::fromValue(MemoryPriority::Normal)); + m_comboBoxOSMemoryPriority.addItem(tr("Below normal"), QVariant::fromValue(MemoryPriority::BelowNormal)); + m_comboBoxOSMemoryPriority.addItem(tr("Medium"), QVariant::fromValue(MemoryPriority::Medium)); + m_comboBoxOSMemoryPriority.addItem(tr("Low"), QVariant::fromValue(MemoryPriority::Low)); + m_comboBoxOSMemoryPriority.addItem(tr("Very low"), QVariant::fromValue(MemoryPriority::VeryLow)); + m_comboBoxOSMemoryPriority.setCurrentIndex(m_comboBoxOSMemoryPriority.findData(QVariant::fromValue(app()->processMemoryPriority()))); addRow(OS_MEMORY_PRIORITY, (tr("Process memory priority (Windows >= 8 only)") + u' ' + makeLink(u"https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-memory_priority_information", u"(?)")) , &m_comboBoxOSMemoryPriority);