Browse Source

Allow to set "working set limit" on non-Windows OS

PR #16874.
adaptive-webui-19844
Chocobo1 3 years ago committed by GitHub
parent
commit
bc937d38a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 41
      src/app/application.cpp
  2. 6
      src/app/application.h
  3. 4
      src/base/interfaces/iapplication.h
  4. 24
      src/gui/advancedsettings.cpp
  5. 3
      src/gui/advancedsettings.h
  6. 6
      src/webui/api/appcontroller.cpp
  7. 2
      src/webui/webapplication.h
  8. 10
      src/webui/www/private/views/preferences.html

41
src/app/application.cpp

@ -39,6 +39,8 @@
#include <memory> #include <memory>
#include <Windows.h> #include <Windows.h>
#include <Shellapi.h> #include <Shellapi.h>
#elif defined(Q_OS_UNIX)
#include <sys/resource.h>
#endif #endif
#include <QAtomicInt> #include <QAtomicInt>
@ -127,9 +129,7 @@ Application::Application(int &argc, char **argv)
, m_storeFileLoggerAge(FILELOGGER_SETTINGS_KEY(u"Age"_qs)) , m_storeFileLoggerAge(FILELOGGER_SETTINGS_KEY(u"Age"_qs))
, m_storeFileLoggerAgeType(FILELOGGER_SETTINGS_KEY(u"AgeType"_qs)) , m_storeFileLoggerAgeType(FILELOGGER_SETTINGS_KEY(u"AgeType"_qs))
, m_storeFileLoggerPath(FILELOGGER_SETTINGS_KEY(u"Path"_qs)) , m_storeFileLoggerPath(FILELOGGER_SETTINGS_KEY(u"Path"_qs))
#ifdef Q_OS_WIN
, m_storeMemoryWorkingSetLimit(SETTINGS_KEY(u"MemoryWorkingSetLimit"_qs)) , m_storeMemoryWorkingSetLimit(SETTINGS_KEY(u"MemoryWorkingSetLimit"_qs))
#endif
{ {
qRegisterMetaType<Log::Msg>("Log::Msg"); qRegisterMetaType<Log::Msg>("Log::Msg");
qRegisterMetaType<Log::Peer>("Log::Peer"); qRegisterMetaType<Log::Peer>("Log::Peer");
@ -206,7 +206,6 @@ const QBtCommandLineParameters &Application::commandLineArgs() const
return m_commandLineArgs; return m_commandLineArgs;
} }
#ifdef Q_OS_WIN
int Application::memoryWorkingSetLimit() const int Application::memoryWorkingSetLimit() const
{ {
return m_storeMemoryWorkingSetLimit.get(512); return m_storeMemoryWorkingSetLimit.get(512);
@ -220,7 +219,6 @@ void Application::setMemoryWorkingSetLimit(const int size)
m_storeMemoryWorkingSetLimit = size; m_storeMemoryWorkingSetLimit = size;
applyMemoryWorkingSetLimit(); applyMemoryWorkingSetLimit();
} }
#endif
bool Application::isFileLoggerEnabled() const bool Application::isFileLoggerEnabled() const
{ {
@ -598,9 +596,7 @@ void Application::processParams(const QStringList &params)
int Application::exec(const QStringList &params) int Application::exec(const QStringList &params)
{ {
#ifdef Q_OS_WIN
applyMemoryWorkingSetLimit(); applyMemoryWorkingSetLimit();
#endif
Net::ProxyConfigurationManager::initInstance(); Net::ProxyConfigurationManager::initInstance();
Net::DownloadManager::initInstance(); Net::DownloadManager::initInstance();
@ -771,28 +767,43 @@ void Application::shutdownCleanup(QSessionManager &manager)
} }
#endif #endif
#ifdef Q_OS_WIN
void Application::applyMemoryWorkingSetLimit() void Application::applyMemoryWorkingSetLimit()
{ {
const SIZE_T UNIT_SIZE = 1024 * 1024; // MiB const size_t MiB = 1024 * 1024;
const SIZE_T maxSize = memoryWorkingSetLimit() * UNIT_SIZE; const QString logMessage = tr("Failed to set physical memory (RAM) usage limit. Error code: %1. Error message: \"%2\"");
const SIZE_T minSize = std::min<SIZE_T>((64 * UNIT_SIZE), (maxSize / 2));
#ifdef Q_OS_WIN
const SIZE_T maxSize = memoryWorkingSetLimit() * MiB;
const SIZE_T minSize = std::min((64 * MiB), (maxSize / 2));
if (!::SetProcessWorkingSetSizeEx(::GetCurrentProcess(), minSize, maxSize, QUOTA_LIMITS_HARDWS_MAX_ENABLE)) if (!::SetProcessWorkingSetSizeEx(::GetCurrentProcess(), minSize, maxSize, QUOTA_LIMITS_HARDWS_MAX_ENABLE))
{ {
const DWORD errorCode = ::GetLastError(); const DWORD errorCode = ::GetLastError();
QString message; QString message;
LPVOID lpMsgBuf = nullptr; LPVOID lpMsgBuf = nullptr;
if (::FormatMessageW((FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS) const DWORD msgLength = ::FormatMessageW((FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS)
, nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&lpMsgBuf), 0, nullptr)) , nullptr, errorCode, LANG_USER_DEFAULT, reinterpret_cast<LPWSTR>(&lpMsgBuf), 0, nullptr);
if (msgLength > 0)
{ {
message = QString::fromWCharArray(reinterpret_cast<LPWSTR>(lpMsgBuf)).trimmed(); message = QString::fromWCharArray(reinterpret_cast<LPWSTR>(lpMsgBuf)).trimmed();
::LocalFree(lpMsgBuf); ::LocalFree(lpMsgBuf);
} }
LogMsg(tr("Failed to set physical memory (RAM) usage limit. Error code: %1. Error message: \"%2\"") LogMsg(logMessage.arg(QString::number(errorCode), message), Log::WARNING);
.arg(QString::number(errorCode), message), Log::WARNING); }
#elif defined(Q_OS_UNIX)
// has no effect on linux but it might be meaningful for other OS
rlimit limit {};
if (::getrlimit(RLIMIT_RSS, &limit) != 0)
return;
limit.rlim_cur = memoryWorkingSetLimit() * MiB;
if (::setrlimit(RLIMIT_RSS, &limit) != 0)
{
const auto message = QString::fromLocal8Bit(strerror(errno));
LogMsg(logMessage.arg(QString::number(errno), message), Log::WARNING);
} }
}
#endif #endif
}
void Application::cleanup() void Application::cleanup()
{ {

6
src/app/application.h

@ -113,10 +113,8 @@ public:
int fileLoggerAgeType() const override; int fileLoggerAgeType() const override;
void setFileLoggerAgeType(int value) override; void setFileLoggerAgeType(int value) override;
#ifdef Q_OS_WIN
int memoryWorkingSetLimit() const override; int memoryWorkingSetLimit() const override;
void setMemoryWorkingSetLimit(int size) override; void setMemoryWorkingSetLimit(int size) override;
#endif
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
QPointer<MainWindow> mainWindow() override; QPointer<MainWindow> mainWindow() override;
@ -137,9 +135,7 @@ private:
void processParams(const QStringList &params); void processParams(const QStringList &params);
void runExternalProgram(const BitTorrent::Torrent *torrent) const; void runExternalProgram(const BitTorrent::Torrent *torrent) const;
void sendNotificationEmail(const BitTorrent::Torrent *torrent); void sendNotificationEmail(const BitTorrent::Torrent *torrent);
#ifdef Q_OS_WIN
void applyMemoryWorkingSetLimit(); void applyMemoryWorkingSetLimit();
#endif
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
@ -166,9 +162,7 @@ private:
SettingValue<int> m_storeFileLoggerAge; SettingValue<int> m_storeFileLoggerAge;
SettingValue<int> m_storeFileLoggerAgeType; SettingValue<int> m_storeFileLoggerAgeType;
SettingValue<Path> m_storeFileLoggerPath; SettingValue<Path> m_storeFileLoggerPath;
#ifdef Q_OS_WIN
SettingValue<int> m_storeMemoryWorkingSetLimit; SettingValue<int> m_storeMemoryWorkingSetLimit;
#endif
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
QPointer<MainWindow> m_window; QPointer<MainWindow> m_window;

4
src/base/interfaces/iapplication.h

@ -30,8 +30,6 @@
#pragma once #pragma once
#include <QtGlobal>
class QString; class QString;
class Path; class Path;
@ -58,8 +56,6 @@ public:
virtual int fileLoggerAgeType() const = 0; virtual int fileLoggerAgeType() const = 0;
virtual void setFileLoggerAgeType(int value) = 0; virtual void setFileLoggerAgeType(int value) = 0;
#ifdef Q_OS_WIN
virtual int memoryWorkingSetLimit() const = 0; virtual int memoryWorkingSetLimit() const = 0;
virtual void setMemoryWorkingSetLimit(int size) = 0; virtual void setMemoryWorkingSetLimit(int size) = 0;
#endif
}; };

24
src/gui/advancedsettings.cpp

@ -62,9 +62,9 @@ namespace
// qBittorrent section // qBittorrent section
QBITTORRENT_HEADER, QBITTORRENT_HEADER,
RESUME_DATA_STORAGE, RESUME_DATA_STORAGE,
MEMORY_WORKING_SET_LIMIT,
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
OS_MEMORY_PRIORITY, OS_MEMORY_PRIORITY,
MEMORY_WORKING_SET_LIMIT,
#endif #endif
// network interface // network interface
NETWORK_IFACE, NETWORK_IFACE,
@ -177,6 +177,8 @@ void AdvancedSettings::saveAdvancedSettings()
? BitTorrent::ResumeDataStorageType::Legacy ? BitTorrent::ResumeDataStorageType::Legacy
: BitTorrent::ResumeDataStorageType::SQLite); : BitTorrent::ResumeDataStorageType::SQLite);
// Physical memory (RAM) usage limit
dynamic_cast<IApplication *>(QCoreApplication::instance())->setMemoryWorkingSetLimit(m_spinBoxMemoryWorkingSetLimit.value());
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
BitTorrent::OSMemoryPriority prio = BitTorrent::OSMemoryPriority::Normal; BitTorrent::OSMemoryPriority prio = BitTorrent::OSMemoryPriority::Normal;
switch (m_comboBoxOSMemoryPriority.currentIndex()) switch (m_comboBoxOSMemoryPriority.currentIndex())
@ -199,8 +201,6 @@ void AdvancedSettings::saveAdvancedSettings()
break; break;
} }
session->setOSMemoryPriority(prio); session->setOSMemoryPriority(prio);
dynamic_cast<IApplication *>(QCoreApplication::instance())->setMemoryWorkingSetLimit(m_spinBoxMemoryWorkingSetLimit.value());
#endif #endif
// Async IO threads // Async IO threads
session->setAsyncIOThreads(m_spinBoxAsyncIOThreads.value()); session->setAsyncIOThreads(m_spinBoxAsyncIOThreads.value());
@ -419,6 +419,14 @@ void AdvancedSettings::loadAdvancedSettings()
m_comboBoxResumeDataStorage.setCurrentIndex((session->resumeDataStorageType() == BitTorrent::ResumeDataStorageType::Legacy) ? 0 : 1); m_comboBoxResumeDataStorage.setCurrentIndex((session->resumeDataStorageType() == BitTorrent::ResumeDataStorageType::Legacy) ? 0 : 1);
addRow(RESUME_DATA_STORAGE, tr("Resume data storage type (requires restart)"), &m_comboBoxResumeDataStorage); addRow(RESUME_DATA_STORAGE, tr("Resume data storage type (requires restart)"), &m_comboBoxResumeDataStorage);
// Physical memory (RAM) usage limit
m_spinBoxMemoryWorkingSetLimit.setMinimum(1);
m_spinBoxMemoryWorkingSetLimit.setMaximum(std::numeric_limits<int>::max());
m_spinBoxMemoryWorkingSetLimit.setSuffix(tr(" MiB"));
m_spinBoxMemoryWorkingSetLimit.setToolTip(tr("This option is less effective on Linux"));
m_spinBoxMemoryWorkingSetLimit.setValue(dynamic_cast<IApplication *>(QCoreApplication::instance())->memoryWorkingSetLimit());
addRow(MEMORY_WORKING_SET_LIMIT, (tr("Physical memory (RAM) usage limit") + u' ' + makeLink(u"https://wikipedia.org/wiki/Working_set", u"(?)"))
, &m_spinBoxMemoryWorkingSetLimit);
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
m_comboBoxOSMemoryPriority.addItems({tr("Normal"), tr("Below normal"), tr("Medium"), tr("Low"), tr("Very low")}); m_comboBoxOSMemoryPriority.addItems({tr("Normal"), tr("Below normal"), tr("Medium"), tr("Low"), tr("Very low")});
int OSMemoryPriorityIndex = 0; int OSMemoryPriorityIndex = 0;
@ -445,17 +453,7 @@ void AdvancedSettings::loadAdvancedSettings()
addRow(OS_MEMORY_PRIORITY, (tr("Process memory priority (Windows >= 8 only)") 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"(?)")) + u' ' + makeLink(u"https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-memory_priority_information", u"(?)"))
, &m_comboBoxOSMemoryPriority); , &m_comboBoxOSMemoryPriority);
m_spinBoxMemoryWorkingSetLimit.setMinimum(1);
m_spinBoxMemoryWorkingSetLimit.setMaximum(std::numeric_limits<int>::max());
m_spinBoxMemoryWorkingSetLimit.setSuffix(tr(" MiB"));
m_spinBoxMemoryWorkingSetLimit.setValue(dynamic_cast<IApplication *>(QCoreApplication::instance())->memoryWorkingSetLimit());
addRow(MEMORY_WORKING_SET_LIMIT, (tr("Physical memory (RAM) usage limit")
+ u' ' + makeLink(u"https://wikipedia.org/wiki/Working_set", u"(?)"))
, &m_spinBoxMemoryWorkingSetLimit);
#endif #endif
// Async IO threads // Async IO threads
m_spinBoxAsyncIOThreads.setMinimum(1); m_spinBoxAsyncIOThreads.setMinimum(1);
m_spinBoxAsyncIOThreads.setMaximum(1024); m_spinBoxAsyncIOThreads.setMaximum(1024);

3
src/gui/advancedsettings.h

@ -58,7 +58,7 @@ private:
void loadAdvancedSettings(); void loadAdvancedSettings();
template <typename T> void addRow(int row, const QString &text, T *widget); template <typename T> void addRow(int row, const QString &text, T *widget);
QSpinBox m_spinBoxAsyncIOThreads, m_spinBoxFilePoolSize, m_spinBoxCheckingMemUsage, m_spinBoxDiskQueueSize, QSpinBox m_spinBoxMemoryWorkingSetLimit, m_spinBoxAsyncIOThreads, m_spinBoxFilePoolSize, m_spinBoxCheckingMemUsage, m_spinBoxDiskQueueSize,
m_spinBoxSaveResumeDataInterval, m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration, m_spinBoxPeerToS, m_spinBoxSaveResumeDataInterval, m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration, m_spinBoxPeerToS,
m_spinBoxListRefresh, m_spinBoxTrackerPort, m_spinBoxSendBufferWatermark, m_spinBoxSendBufferLowWatermark, m_spinBoxListRefresh, m_spinBoxTrackerPort, m_spinBoxSendBufferWatermark, m_spinBoxSendBufferLowWatermark,
m_spinBoxSendBufferWatermarkFactor, m_spinBoxConnectionSpeed, m_spinBoxSocketBacklogSize, m_spinBoxMaxConcurrentHTTPAnnounces, m_spinBoxStopTrackerTimeout, m_spinBoxSendBufferWatermarkFactor, m_spinBoxConnectionSpeed, m_spinBoxSocketBacklogSize, m_spinBoxMaxConcurrentHTTPAnnounces, m_spinBoxStopTrackerTimeout,
@ -82,7 +82,6 @@ private:
// OS dependent settings // OS dependent settings
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
QComboBox m_comboBoxOSMemoryPriority; QComboBox m_comboBoxOSMemoryPriority;
QSpinBox m_spinBoxMemoryWorkingSetLimit;
#endif #endif
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS

6
src/webui/api/appcontroller.cpp

@ -45,6 +45,7 @@
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/global.h" #include "base/global.h"
#include "base/interfaces/iapplication.h"
#include "base/net/portforwarder.h" #include "base/net/portforwarder.h"
#include "base/net/proxyconfigurationmanager.h" #include "base/net/proxyconfigurationmanager.h"
#include "base/path.h" #include "base/path.h"
@ -285,6 +286,8 @@ void AppController::preferencesAction()
// Advanced settings // Advanced settings
// qBitorrent preferences // qBitorrent preferences
// Physical memory (RAM) usage limit
data[u"memory_working_set_limit"_qs] = dynamic_cast<IApplication *>(QCoreApplication::instance())->memoryWorkingSetLimit();
// Current network interface // Current network interface
data[u"current_network_interface"_qs] = session->networkInterface(); data[u"current_network_interface"_qs] = session->networkInterface();
// Current network interface address // Current network interface address
@ -738,6 +741,9 @@ void AppController::setPreferencesAction()
// Advanced settings // Advanced settings
// qBittorrent preferences // qBittorrent preferences
// Physical memory (RAM) usage limit
if (hasKey(u"memory_working_set_limit"_qs))
dynamic_cast<IApplication *>(QCoreApplication::instance())->setMemoryWorkingSetLimit(it.value().toInt());
// Current network interface // Current network interface
if (hasKey(u"current_network_interface"_qs)) if (hasKey(u"current_network_interface"_qs))
{ {

2
src/webui/webapplication.h

@ -48,7 +48,7 @@
#include "base/utils/version.h" #include "base/utils/version.h"
#include "api/isessionmanager.h" #include "api/isessionmanager.h"
inline const Utils::Version<int, 3, 2> API_VERSION {2, 8, 9}; inline const Utils::Version<int, 3, 2> API_VERSION {2, 8, 10};
class APIController; class APIController;
class AuthController; class AuthController;

10
src/webui/www/private/views/preferences.html

@ -903,6 +903,14 @@
<fieldset class="settings"> <fieldset class="settings">
<legend>QBT_TR(qBittorrent Section)QBT_TR[CONTEXT=OptionsDialog]&nbsp;(<a href="https://github.com/qbittorrent/qBittorrent/wiki/Explanation-of-Options-in-qBittorrent#Advanced" target="_blank">QBT_TR(Open documentation)QBT_TR[CONTEXT=HttpServer]</a>)</legend> <legend>QBT_TR(qBittorrent Section)QBT_TR[CONTEXT=OptionsDialog]&nbsp;(<a href="https://github.com/qbittorrent/qBittorrent/wiki/Explanation-of-Options-in-qBittorrent#Advanced" target="_blank">QBT_TR(Open documentation)QBT_TR[CONTEXT=HttpServer]</a>)</legend>
<table> <table>
<tr>
<td>
<label for="memoryWorkingSetLimit">QBT_TR(Physical memory (RAM) usage limit:)QBT_TR[CONTEXT=OptionsDialog]&nbsp;<a href="https://wikipedia.org/wiki/Working_set" target="_blank">(?)</a></label>
</td>
<td>
<input type="text" id="memoryWorkingSetLimit" style="width: 15em;" title="QBT_TR(This option is less effective on Linux)QBT_TR[CONTEXT=OptionsDialog]">&nbsp;&nbsp;QBT_TR(MiB)QBT_TR[CONTEXT=OptionsDialog]
</td>
</tr>
<tr> <tr>
<td> <td>
<label for="networkInterface">QBT_TR(Network interface:)QBT_TR[CONTEXT=OptionsDialog]</label> <label for="networkInterface">QBT_TR(Network interface:)QBT_TR[CONTEXT=OptionsDialog]</label>
@ -1947,6 +1955,7 @@
// Advanced settings // Advanced settings
// qBittorrent section // qBittorrent section
$('memoryWorkingSetLimit').setProperty('value', pref.memory_working_set_limit);
updateNetworkInterfaces(pref.current_network_interface); updateNetworkInterfaces(pref.current_network_interface);
updateInterfaceAddresses(pref.current_network_interface, pref.current_interface_address); updateInterfaceAddresses(pref.current_network_interface, pref.current_interface_address);
$('saveResumeDataInterval').setProperty('value', pref.save_resume_data_interval); $('saveResumeDataInterval').setProperty('value', pref.save_resume_data_interval);
@ -2346,6 +2355,7 @@
// Update advanced settings // Update advanced settings
// qBittorrent section // qBittorrent section
settings.set('memory_working_set_limit', $('memoryWorkingSetLimit').getProperty('value'));
settings.set('current_network_interface', $('networkInterface').getProperty('value')); settings.set('current_network_interface', $('networkInterface').getProperty('value'));
settings.set('current_interface_address', $('optionalIPAddressToBind').getProperty('value')); settings.set('current_interface_address', $('optionalIPAddressToBind').getProperty('value'));
settings.set('save_resume_data_interval', $('saveResumeDataInterval').getProperty('value')); settings.set('save_resume_data_interval', $('saveResumeDataInterval').getProperty('value'));

Loading…
Cancel
Save