mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-01 21:28:07 -06:00
It was reported that qbittorrent crashes on Mac OS X when user deletes torrents from label view when label filter is active. The callstack of the crash is the following: 1 abort + 129 2 __assert_rtn + 321 3 QTorrentHandle::total_size() const + 124 4 TorrentModelItem::data(int, int) const + 307 5 TorrentModel::data(QModelIndex const&, int) const + 255 6 QSortFilterProxyModel::data(QModelIndex const&, int) const + 109 7 QSortFilterProxyModel::data(QModelIndex const&, int) const + 109 8 QSortFilterProxyModel::data(QModelIndex const&, int) const + 109 9 QItemDelegate::rect(QStyleOptionViewItem const&, QModelIndex const&, int) const + 75 10 QItemDelegate::sizeHint(QStyleOptionViewItem const&, QModelIndex const&) const + 172 11 TransferListDelegate::sizeHint(QStyleOptionViewItem const&, QModelIndex const&) const + 14 12 QTreeView::indexRowSizeHint(QModelIndex const&) const + 887 13 QTreeViewPrivate::layout(int, bool, bool) + 462 14 QTreeView::doItemsLayout() + 356 15 QTreeViewPrivate::updateScrollBars() + 109 16 QTreeView::scrollTo(QModelIndex const&, QAbstractItemView::ScrollHint) + 124 17 TransferListWidget::currentChanged(QModelIndex const&, QModelIndex const&) + 548 18 TransferListWidget::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) + 641 19 QMetaObject::activate(QObject*, QMetaObject const*, int, void**) + 2196 20 QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(QModelIndex const&, int, int) + 3729 21 QItemSelectionModel::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) + 398 22 QMetaObject::activate(QObject*, QMetaObject const*, int, void**) + 2196 23 QAbstractItemModel::rowsAboutToBeRemoved(QModelIndex const&, int, int) + 78 24 QAbstractItemModel::beginRemoveRows(QModelIndex const&, int, int) + 106 25 QSortFilterProxyModelPrivate::remove_proxy_interval(QVector<int>&, QVector<int>&, int, int, QModelIndex const&, Qt::Orientation, bool) + 58 26 QSortFilterProxyModelPrivate::remove_source_items(QVector<int>&, QVector<int>&, QVector<int> const&, QModelIndex const&, Qt::Orientation, bool) + 265 27 QSortFilterProxyModelPrivate::source_items_about_to_be_removed(QModelIndex const&, int, int, Qt::Orientation) + 232 28 QMetaObject::activate(QObject*, QMetaObject const*, int, void**) + 2196 29 QAbstractItemModel::rowsAboutToBeRemoved(QModelIndex const&, int, int) + 78 30 QAbstractItemModel::beginRemoveRows(QModelIndex const&, int, int) + 106 31 QSortFilterProxyModelPrivate::remove_proxy_interval(QVector<int>&, QVector<int>&, int, int, QModelIndex const&, Qt::Orientation, bool) + 58 32 QSortFilterProxyModelPrivate::remove_source_items(QVector<int>&, QVector<int>&, QVector<int> const&, QModelIndex const&, Qt::Orientation, bool) + 265 33 QSortFilterProxyModelPrivate::source_items_about_to_be_removed(QModelIndex const&, int, int, Qt::Orientation) + 232 34 QMetaObject::activate(QObject*, QMetaObject const*, int, void**) + 2196 35 QAbstractItemModel::rowsAboutToBeRemoved(QModelIndex const&, int, int) + 78 36 QAbstractItemModel::beginRemoveRows(QModelIndex const&, int, int) + 106 37 QSortFilterProxyModelPrivate::remove_proxy_interval(QVector<int>&, QVector<int>&, int, int, QModelIndex const&, Qt::Orientation, bool) + 58 38 QSortFilterProxyModelPrivate::remove_source_items(QVector<int>&, QVector<int>&, QVector<int> const&, QModelIndex const&, Qt::Orientation, bool) + 265 39 QSortFilterProxyModelPrivate::source_items_about_to_be_removed(QModelIndex const&, int, int, Qt::Orientation) + 232 40 QMetaObject::activate(QObject*, QMetaObject const*, int, void**) + 2196 41 QAbstractItemModel::rowsAboutToBeRemoved(QModelIndex const&, int, int) + 78 42 QAbstractItemModel::beginRemoveRows(QModelIndex const&, int, int) + 106 43 TorrentModel::removeTorrent(QString const&) + 81 44 TorrentModel::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) + 345 45 QMetaObject::activate(QObject*, QMetaObject const*, int, void**) + 2196 46 QBtSession::deletedTorrent(QString const&) + 56 47 QBtSession::deleteTorrent(QString const&, bool) + 2855 48 TransferListWidget::deleteSelectedTorrents() + 292 49 TransferListWidget::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) + 230 50 QMetaObject::activate(QObject*, QMetaObject const*, int, void**) + 2196 51 QAction::activate(QAction::ActionEvent) + 227 52 QMenuPrivate::activateCausedStack(QList<QPointer<QWidget> > const&, QAction*, QAction::ActionEvent, bool) + 77 53 QMenuPrivate::activateAction(QAction*, QAction::ActionEvent, bool) + 470 54 QWidget::event(QEvent*) + 687 55 QMenu::event(QEvent*) + 617 56 QApplicationPrivate::notify_helper(QObject*, QEvent*) + 194 57 QApplication::notify(QObject*, QEvent*) + 2716 58 SessionApplication::notify(QObject*, QEvent*) + 21 59 QCoreApplication::notifyInternal(QObject*, QEvent*) + 118 60 QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool) + 448 61 qt_mac_handleMouseEvent(NSEvent*, QEvent::Type, Qt::MouseButton, QWidget*, bool) + 1300 62 -[NSWindow _reallySendEvent:] + 759 63 -[NSWindow sendEvent:] + 368 64 -[QCocoaPanel sendEvent:] + 113 65 -[NSApplication sendEvent:] + 2238 66 -[QNSApplication sendEvent:] + 97 67 -[NSApplication run] + 711 68 QEventDispatcherMac::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 1522 69 QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 77 70 QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 370 71 QMenu::exec(QPoint const&, QAction*) + 103 72 TransferListWidget::displayListMenu(QPoint const&) + 8741 73 TransferListWidget::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) + 622 74 QMetaObject::activate(QObject*, QMetaObject const*, int, void**) + 2196 75 QWidget::event(QEvent*) + 3082 76 QFrame::event(QEvent*) + 45 77 QAbstractScrollArea::viewportEvent(QEvent*) + 108 78 QAbstractItemView::viewportEvent(QEvent*) + 1390 79 QTreeView::viewportEvent(QEvent*) + 218 80 QAbstractScrollAreaFilter::eventFilter(QObject*, QEvent*) + 37 81 QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*, QEvent*) + 115 82 QApplicationPrivate::notify_helper(QObject*, QEvent*) + 178 83 QApplication::notify(QObject*, QEvent*) + 5742 84 SessionApplication::notify(QObject*, QEvent*) + 21 85 QCoreApplication::notifyInternal(QObject*, QEvent*) + 118 86 qt_sendSpontaneousEvent(QObject*, QEvent*) + 45 87 qt_mac_handleMouseEvent(NSEvent*, QEvent::Type, Qt::MouseButton, QWidget*, bool) + 1378 88 -[NSWindow _reallySendEvent:] + 5682 89 -[NSWindow sendEvent:] + 368 90 -[QCocoaWindow sendEvent:] + 113 91 -[NSApplication sendEvent:] + 2238 92 -[QNSApplication sendEvent:] + 97 93 -[NSApplication run] + 711 94 QEventDispatcherMac::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 1522 95 QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 77 96 QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 370 97 QCoreApplication::exec() + 199 98 main + 3415 99 start + 52 As we can see the user deleted some torrent (48). QBtSession deleted the torrent from libtorrent::session (47) and emitted a signal (46), about torrent deletion. In responce to the signal (43) the TorrentModel notifies (42) its views about a change. After a long chain of notifications (42-6) the view requested (5) a value of total_size from TorrentModel. QTorrentHandle is already invalid as the torrent was removed in (47). So we've got a crash in (3). The fix is relatively straightforward: do notify TorrentModel about removal not after, but before torrent is removed from libtorrent::session. This commit does the same thing to TorrentSpeedMonitor. This bug reveals a major flaw in a design: currently we have a several components all subscribed to the torrent removal signal. Signal is delivered to them in arbitrary order, but they access each other in the handlers of this signal. E.g. TorrentModel can access TorrentSpeedMonitor. This doesn't lead to a crash because TorrentSpeedMonitor returns MAX_ETA when ETA is queried for unknown torrent.
128 lines
5.1 KiB
C++
128 lines
5.1 KiB
C++
/*
|
|
* Bittorrent Client using Qt4 and libtorrent.
|
|
* Copyright (C) 2010 Christophe Dumez
|
|
*
|
|
* 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.
|
|
*
|
|
* Contact : chris@qbittorrent.org
|
|
*/
|
|
|
|
#ifndef TORRENTMODEL_H
|
|
#define TORRENTMODEL_H
|
|
|
|
#include <QAbstractListModel>
|
|
#include <QList>
|
|
#include <QDateTime>
|
|
#include <QIcon>
|
|
#include <QTimer>
|
|
|
|
#include "qtorrenthandle.h"
|
|
|
|
struct TorrentStatusReport {
|
|
TorrentStatusReport(): nb_downloading(0), nb_seeding(0), nb_active(0), nb_inactive(0), nb_paused(0) {}
|
|
uint nb_downloading; uint nb_seeding; uint nb_active; uint nb_inactive; uint nb_paused;
|
|
};
|
|
|
|
class TorrentModelItem : public QObject {
|
|
Q_OBJECT
|
|
|
|
public:
|
|
enum State {STATE_DOWNLOADING, STATE_DOWNLOADING_META, STATE_ALLOCATING, STATE_STALLED_DL, STATE_STALLED_UP, STATE_SEEDING, STATE_PAUSED_DL, STATE_PAUSED_UP, STATE_QUEUED_DL, STATE_QUEUED_UP, STATE_CHECKING_UP, STATE_CHECKING_DL, STATE_QUEUED_CHECK, STATE_QUEUED_FASTCHECK, STATE_INVALID};
|
|
enum Column {TR_NAME, TR_PRIORITY, TR_SIZE, TR_TOTAL_SIZE, TR_PROGRESS, TR_STATUS, TR_SEEDS, TR_PEERS, TR_DLSPEED, TR_UPSPEED, TR_ETA, TR_RATIO, TR_LABEL, TR_ADD_DATE, TR_SEED_DATE, TR_TRACKER, TR_DLLIMIT, TR_UPLIMIT, TR_AMOUNT_DOWNLOADED, TR_AMOUNT_UPLOADED, TR_AMOUNT_LEFT, TR_TIME_ELAPSED, TR_SAVE_PATH, TR_COMPLETED, TR_RATIO_LIMIT, TR_SEEN_COMPLETE_DATE, TR_LAST_ACTIVITY, NB_COLUMNS};
|
|
|
|
public:
|
|
TorrentModelItem(const QTorrentHandle& h);
|
|
void refreshStatus(libtorrent::torrent_status const& status);
|
|
inline int columnCount() const { return NB_COLUMNS; }
|
|
QVariant data(int column, int role = Qt::DisplayRole) const;
|
|
bool setData(int column, const QVariant &value, int role = Qt::DisplayRole);
|
|
inline QString hash() const { return m_hash; }
|
|
State state() const;
|
|
|
|
signals:
|
|
void labelChanged(QString previous, QString current);
|
|
|
|
private:
|
|
static QIcon getIconByState(State state);
|
|
static QColor getColorByState(State state);
|
|
|
|
private:
|
|
QTorrentHandle m_torrent;
|
|
libtorrent::torrent_status m_lastStatus;
|
|
QDateTime m_addedTime;
|
|
QString m_label;
|
|
QString m_name;
|
|
QString m_hash; // Cached for safety reasons
|
|
};
|
|
|
|
class TorrentModel : public QAbstractListModel
|
|
{
|
|
Q_OBJECT
|
|
Q_DISABLE_COPY(TorrentModel)
|
|
|
|
public:
|
|
explicit TorrentModel(QObject *parent = 0);
|
|
~TorrentModel();
|
|
inline int rowCount(const QModelIndex& index = QModelIndex()) const { Q_UNUSED(index); return m_torrents.size(); }
|
|
int columnCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return TorrentModelItem::NB_COLUMNS; }
|
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
|
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole);
|
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
|
int torrentRow(const QString &hash) const;
|
|
QString torrentHash(int row) const;
|
|
void setRefreshInterval(int refreshInterval);
|
|
TorrentStatusReport getTorrentStatusReport() const;
|
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
|
void populate();
|
|
bool inhibitSystem();
|
|
|
|
signals:
|
|
void torrentAdded(TorrentModelItem *torrentItem);
|
|
void torrentAboutToBeRemoved(TorrentModelItem *torrentItem);
|
|
void torrentChangedLabel(TorrentModelItem *torrentItem, QString previous, QString current);
|
|
|
|
private slots:
|
|
void addTorrent(const QTorrentHandle& h);
|
|
void handleTorrentUpdate(const QTorrentHandle &h);
|
|
void handleFinishedTorrent(const QTorrentHandle& h);
|
|
void notifyTorrentChanged(int row);
|
|
void forceModelRefresh();
|
|
void handleTorrentLabelChange(QString previous, QString current);
|
|
void handleTorrentAboutToBeRemoved(const QTorrentHandle & h);
|
|
void stateUpdated(const std::vector<libtorrent::torrent_status> &statuses);
|
|
|
|
private:
|
|
void beginInsertTorrent(int row);
|
|
void endInsertTorrent();
|
|
void beginRemoveTorrent(int row);
|
|
void endRemoveTorrent();
|
|
|
|
private:
|
|
QList<TorrentModelItem*> m_torrents;
|
|
int m_refreshInterval;
|
|
QTimer m_refreshTimer;
|
|
};
|
|
|
|
#endif // TORRENTMODEL_H
|