diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index ca5107d67..d4d3e758a 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -256,6 +256,8 @@ namespace BitTorrent virtual void setPerformanceWarningEnabled(bool enable) = 0; virtual int saveResumeDataInterval() const = 0; virtual void setSaveResumeDataInterval(int value) = 0; + virtual int shutdownTimeout() const = 0; + virtual void setShutdownTimeout(int value) = 0; virtual int port() const = 0; virtual void setPort(int port) = 0; virtual bool isSSLEnabled() const = 0; diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index dde72af85..7294f678d 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -58,6 +58,7 @@ #include #include +#include #include #include #include @@ -480,6 +481,7 @@ SessionImpl::SessionImpl(QObject *parent) , m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY(u"BandwidthSchedulerEnabled"_s), false) , m_isPerformanceWarningEnabled(BITTORRENT_SESSION_KEY(u"PerformanceWarning"_s), false) , m_saveResumeDataInterval(BITTORRENT_SESSION_KEY(u"SaveResumeDataInterval"_s), 60) + , m_shutdownTimeout(BITTORRENT_SESSION_KEY(u"ShutdownTimeout"_s), -1) , m_port(BITTORRENT_SESSION_KEY(u"Port"_s), -1) , m_sslEnabled(BITTORRENT_SESSION_KEY(u"SSL/Enabled"_s), false) , m_sslPort(BITTORRENT_SESSION_KEY(u"SSL/Port"_s), -1) @@ -602,6 +604,9 @@ SessionImpl::~SessionImpl() { m_nativeSession->pause(); + const qint64 timeout = (m_shutdownTimeout >= 0) ? (m_shutdownTimeout * 1000) : -1; + const QDeadlineTimer shutdownDeadlineTimer {timeout}; + if (m_torrentsQueueChanged) { m_nativeSession->post_torrent_updates({}); @@ -628,8 +633,24 @@ SessionImpl::~SessionImpl() m_asyncWorker->clear(); m_asyncWorker->waitForDone(); - qDebug("Deleting libtorrent session..."); + auto *nativeSessionProxy = new lt::session_proxy(m_nativeSession->abort()); delete m_nativeSession; + + qDebug("Deleting resume data storage..."); + delete m_resumeDataStorage; + LogMsg(tr("Saving resume data completed.")); + + auto *sessionTerminateThread = QThread::create([nativeSessionProxy]() + { + qDebug("Deleting libtorrent session..."); + delete nativeSessionProxy; + }); + connect(sessionTerminateThread, &QThread::finished, sessionTerminateThread, &QObject::deleteLater); + sessionTerminateThread->start(); + if (sessionTerminateThread->wait(shutdownDeadlineTimer)) + LogMsg(tr("BitTorrent session successfully finished.")); + else + LogMsg(tr("Session shutdown timed out.")); } QString SessionImpl::getDHTBootstrapNodes() const @@ -3476,6 +3497,16 @@ void SessionImpl::setSaveResumeDataInterval(const int value) } } +int SessionImpl::shutdownTimeout() const +{ + return m_shutdownTimeout; +} + +void SessionImpl::setShutdownTimeout(const int value) +{ + m_shutdownTimeout = value; +} + int SessionImpl::port() const { return m_port; diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index 334c0baf9..c6e2e85a4 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -233,6 +233,8 @@ namespace BitTorrent void setPerformanceWarningEnabled(bool enable) override; int saveResumeDataInterval() const override; void setSaveResumeDataInterval(int value) override; + int shutdownTimeout() const override; + void setShutdownTimeout(int value) override; int port() const override; void setPort(int port) override; bool isSSLEnabled() const override; @@ -688,6 +690,7 @@ namespace BitTorrent CachedSettingValue m_isBandwidthSchedulerEnabled; CachedSettingValue m_isPerformanceWarningEnabled; CachedSettingValue m_saveResumeDataInterval; + CachedSettingValue m_shutdownTimeout; CachedSettingValue m_port; CachedSettingValue m_sslEnabled; CachedSettingValue m_sslPort; diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index 61d43000e..d81cfecc6 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -106,6 +106,7 @@ namespace #endif // Q_OS_MACOS || Q_OS_WIN PYTHON_EXECUTABLE_PATH, START_SESSION_PAUSED, + SESSION_SHUTDOWN_TIMEOUT, // libtorrent section LIBTORRENT_HEADER, @@ -334,6 +335,8 @@ void AdvancedSettings::saveAdvancedSettings() const pref->setPythonExecutablePath(Path(m_pythonExecutablePath.text().trimmed())); // Start session paused session->setStartPaused(m_checkBoxStartSessionPaused.isChecked()); + // Session shutdown timeout + session->setShutdownTimeout(m_spinBoxSessionShutdownTimeout.value()); // Choking algorithm session->setChokingAlgorithm(m_comboBoxChokingAlgorithm.currentData().value()); // Seed choking algorithm @@ -848,7 +851,16 @@ void AdvancedSettings::loadAdvancedSettings() addRow(PYTHON_EXECUTABLE_PATH, tr("Python executable path (may require restart)"), &m_pythonExecutablePath); // Start session paused m_checkBoxStartSessionPaused.setChecked(session->isStartPaused()); - addRow(START_SESSION_PAUSED, tr("Start session in paused state"), &m_checkBoxStartSessionPaused); + addRow(START_SESSION_PAUSED, tr("Start BitTorrent session in paused state"), &m_checkBoxStartSessionPaused); + // Session shutdown timeout + m_spinBoxSessionShutdownTimeout.setMinimum(-1); + m_spinBoxSessionShutdownTimeout.setMaximum(std::numeric_limits::max()); + m_spinBoxSessionShutdownTimeout.setValue(session->shutdownTimeout()); + m_spinBoxSessionShutdownTimeout.setSuffix(tr(" sec", " seconds")); + m_spinBoxSessionShutdownTimeout.setSpecialValueText(tr("-1 (unlimited)")); + m_spinBoxSessionShutdownTimeout.setToolTip(u"Sets the timeout for the session to be shut down gracefully, at which point it will be forcibly terminated.
Note that this does not apply to the saving resume data time."_s); + addRow(SESSION_SHUTDOWN_TIMEOUT, tr("BitTorrent session shutdown timeout [-1: unlimited]"), &m_spinBoxSessionShutdownTimeout); + // Choking algorithm m_comboBoxChokingAlgorithm.addItem(tr("Fixed slots"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::FixedSlots)); m_comboBoxChokingAlgorithm.addItem(tr("Upload rate based"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::RateBased)); @@ -946,6 +958,7 @@ void AdvancedSettings::addRow(const int row, const QString &text, T *widget) { auto *label = new QLabel(text); label->setOpenExternalLinks(true); + label->setToolTip(widget->toolTip()); setCellWidget(row, PROPERTY, label); setCellWidget(row, VALUE, widget); diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index 1ad82da8c..386a44d10 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -73,7 +73,7 @@ private: m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration, m_spinBoxPeerToS, m_spinBoxListRefresh, m_spinBoxTrackerPort, m_spinBoxSendBufferWatermark, m_spinBoxSendBufferLowWatermark, m_spinBoxSendBufferWatermarkFactor, m_spinBoxConnectionSpeed, m_spinBoxSocketSendBufferSize, m_spinBoxSocketReceiveBufferSize, m_spinBoxSocketBacklogSize, - m_spinBoxMaxConcurrentHTTPAnnounces, m_spinBoxStopTrackerTimeout, + m_spinBoxMaxConcurrentHTTPAnnounces, m_spinBoxStopTrackerTimeout, m_spinBoxSessionShutdownTimeout, m_spinBoxSavePathHistoryLength, m_spinBoxPeerTurnover, m_spinBoxPeerTurnoverCutoff, m_spinBoxPeerTurnoverInterval, m_spinBoxRequestQueueSize; QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts, m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxReannounceWhenAddressChanged, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus,