diff --git a/src/base/base.pri b/src/base/base.pri index b38caa601..e5edf06e4 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -36,6 +36,7 @@ HEADERS += \ $$PWD/bittorrent/private/bandwidthscheduler.h \ $$PWD/bittorrent/private/filterparserthread.h \ $$PWD/bittorrent/private/statistics.h \ + $$PWD/bittorrent/private/resumedatasavingmanager.h \ $$PWD/rss/rssmanager.h \ $$PWD/rss/rssfeed.h \ $$PWD/rss/rssfolder.h \ @@ -87,6 +88,7 @@ SOURCES += \ $$PWD/bittorrent/private/bandwidthscheduler.cpp \ $$PWD/bittorrent/private/filterparserthread.cpp \ $$PWD/bittorrent/private/statistics.cpp \ + $$PWD/bittorrent/private/resumedatasavingmanager.cpp \ $$PWD/rss/rssmanager.cpp \ $$PWD/rss/rssfeed.cpp \ $$PWD/rss/rssfolder.cpp \ diff --git a/src/base/bittorrent/private/resumedatasavingmanager.cpp b/src/base/bittorrent/private/resumedatasavingmanager.cpp new file mode 100644 index 000000000..17928eaff --- /dev/null +++ b/src/base/bittorrent/private/resumedatasavingmanager.cpp @@ -0,0 +1,54 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include +#include + +#include "base/utils/fs.h" +#include "resumedatasavingmanager.h" + +ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath) + : m_resumeDataDir(resumeFolderPath) +{ +} + +void ResumeDataSavingManager::saveResumeData(QString infoHash, QByteArray data, int priority) const +{ + QStringList filters(QString("%1.fastresume.*").arg(infoHash)); + const QStringList files = m_resumeDataDir.entryList(filters, QDir::Files, QDir::Unsorted); + foreach (const QString &file, files) + Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(file)); + + QString filename = QString("%1.fastresume.%2").arg(infoHash).arg(priority); + QString filepath = m_resumeDataDir.absoluteFilePath(filename); + + qDebug() << "Saving resume data in" << filepath; + QFile resumeFile(filepath); + if (resumeFile.open(QIODevice::WriteOnly)) + resumeFile.write(data); +} diff --git a/src/base/bittorrent/private/resumedatasavingmanager.h b/src/base/bittorrent/private/resumedatasavingmanager.h new file mode 100644 index 000000000..dae12d40a --- /dev/null +++ b/src/base/bittorrent/private/resumedatasavingmanager.h @@ -0,0 +1,50 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef RESUMEDATASAVINGMANAGER_H +#define RESUMEDATASAVINGMANAGER_H + +#include +#include +#include + +class ResumeDataSavingManager: public QObject +{ + Q_OBJECT + +public: + explicit ResumeDataSavingManager(const QString &resumeFolderPath); + +public slots: + void saveResumeData(QString infoHash, QByteArray data, int priority) const; + +private: + QDir m_resumeDataDir; +}; + +#endif // RESUMEDATASAVINGMANAGER_H diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index a4bb4b64f..50b3a1964 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -43,6 +43,7 @@ using namespace BitTorrent; #include #include #include +#include #include #include @@ -80,6 +81,7 @@ using namespace BitTorrent; #include "private/filterparserthread.h" #include "private/statistics.h" #include "private/bandwidthscheduler.h" +#include "private/resumedatasavingmanager.h" #include "trackerentry.h" #include "tracker.h" #include "magneturi.h" @@ -205,6 +207,11 @@ Session::Session(QObject *parent) connect(&m_networkManager, SIGNAL(configurationRemoved(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&))); connect(&m_networkManager, SIGNAL(configurationChanged(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&))); + m_ioThread = new QThread(this); + m_resumeDataSavingManager = new ResumeDataSavingManager(m_resumeFolderPath); + m_resumeDataSavingManager->moveToThread(m_ioThread); + connect(m_ioThread, SIGNAL(finished()), m_resumeDataSavingManager, SLOT(deleteLater())); + m_ioThread->start(); m_resumeDataTimer->start(); // initialize PortForwarder instance @@ -282,6 +289,9 @@ Session::~Session() qDebug("Deleting the session"); delete m_nativeSession; + m_ioThread->quit(); + m_ioThread->wait(); + m_resumeFolderLock.close(); m_resumeFolderLock.remove(); } @@ -1710,7 +1720,17 @@ void Session::handleTorrentFinished(TorrentHandle *const torrent) void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const libtorrent::entry &data) { --m_numResumeData; - writeResumeDataFile(torrent, data); + + // Separated thread is used for the blocking IO which results in slow processing of many torrents. + // Encoding data in parallel while doing IO saves time. Copying libtorrent::entry objects around + // isn't cheap too. + + QByteArray out; + libt::bencode(std::back_inserter(out), data); + + QMetaObject::invokeMethod(m_resumeDataSavingManager, "saveResumeData", + Q_ARG(QString, torrent->hash()), Q_ARG(QByteArray, out), + Q_ARG(int, torrent->queuePosition())); } void Session::handleTorrentResumeDataFailed(TorrentHandle *const torrent) @@ -2356,28 +2376,6 @@ bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, MagnetUr return true; } -bool Session::writeResumeDataFile(TorrentHandle *const torrent, const libt::entry &data) -{ - const QDir resumeDataDir(m_resumeFolderPath); - - QStringList filters(QString("%1.fastresume.*").arg(torrent->hash())); - const QStringList files = resumeDataDir.entryList(filters, QDir::Files, QDir::Unsorted); - foreach (const QString &file, files) - Utils::Fs::forceRemove(resumeDataDir.absoluteFilePath(file)); - - QString filename = QString("%1.fastresume.%2").arg(torrent->hash()).arg(torrent->queuePosition()); - QString filepath = resumeDataDir.absoluteFilePath(filename); - - qDebug("Saving resume data in %s", qPrintable(filepath)); - QFile resumeFile(filepath); - QVector out; - libt::bencode(std::back_inserter(out), data); - if (resumeFile.open(QIODevice::WriteOnly)) - return (resumeFile.write(&out[0], out.size()) == out.size()); - - return false; -} - void torrentQueuePositionUp(const libt::torrent_handle &handle) { try { diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index ee945512b..b7eb20b65 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -86,6 +86,7 @@ namespace libtorrent struct external_ip_alert; } +class QThread; class QTimer; class QStringList; class QString; @@ -95,6 +96,7 @@ template class QList; class FilterParserThread; class BandwidthScheduler; class Statistics; +class ResumeDataSavingManager; typedef QPair QStringPair; @@ -314,7 +316,6 @@ namespace BitTorrent void handleExternalIPAlert(libtorrent::external_ip_alert *p); void saveResumeData(); - bool writeResumeDataFile(TorrentHandle *const torrent, const libtorrent::entry &data); void dispatchAlerts(std::auto_ptr alertPtr); void getPendingAlerts(QVector &out, ulong time = 0); @@ -355,6 +356,9 @@ namespace BitTorrent QPointer m_bwScheduler; // Tracker QPointer m_tracker; + // fastresume data writing thread + QThread *m_ioThread; + ResumeDataSavingManager *m_resumeDataSavingManager; QHash m_loadedMetadata; QHash m_torrents;