mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
[VisualDeckStorage] Cancel in-flight deck loads and enforce teardown order on destruction
This commit is contained in:
parent
00151380e3
commit
b6dcf1815d
4 changed files with 79 additions and 0 deletions
|
|
@ -137,6 +137,39 @@ VisualDeckStorageModel::VisualDeckStorageModel(QObject *parent) : QAbstractListM
|
|||
{
|
||||
}
|
||||
|
||||
VisualDeckStorageModel::~VisualDeckStorageModel()
|
||||
{
|
||||
// Invalidate every outstanding result and drop queued work.
|
||||
++scanGeneration;
|
||||
pendingLoads.clear();
|
||||
drainScheduled = false;
|
||||
|
||||
// Detach and destroy the watchers of still-running futures explicitly, before
|
||||
// QObject teardown, so none of their finished() lambdas (which capture this)
|
||||
// can fire into a half-destroyed model. Futures that have not started yet are
|
||||
// cancelled; ones already running finish on the pool and their result is dropped.
|
||||
const QList<QPointer<QFutureWatcherBase>> watchers = std::exchange(activeWatchers, {});
|
||||
for (const QPointer<QFutureWatcherBase> &watcher : watchers) {
|
||||
if (!watcher) {
|
||||
continue;
|
||||
}
|
||||
disconnect(watcher, nullptr, this, nullptr);
|
||||
watcher->cancel();
|
||||
delete watcher.data();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageModel::trackWatcher(QFutureWatcherBase *watcher)
|
||||
{
|
||||
activeWatchers.append(QPointer<QFutureWatcherBase>(watcher));
|
||||
}
|
||||
|
||||
void VisualDeckStorageModel::forgetWatcher(QFutureWatcherBase *watcher)
|
||||
{
|
||||
activeWatchers.removeIf(
|
||||
[watcher](const QPointer<QFutureWatcherBase> &tracked) { return !tracked || tracked.data() == watcher; });
|
||||
}
|
||||
|
||||
int VisualDeckStorageModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : decks.size();
|
||||
|
|
@ -245,7 +278,9 @@ void VisualDeckStorageModel::startScan()
|
|||
// The scan (directory walk + one stat per file) runs on a worker thread so that
|
||||
// constructing the widget never stalls the UI thread on a large deck folder.
|
||||
auto *watcher = new QFutureWatcher<DeckScanResult>(this);
|
||||
trackWatcher(watcher);
|
||||
connect(watcher, &QFutureWatcher<DeckScanResult>::finished, this, [this, watcher, generation] {
|
||||
forgetWatcher(watcher);
|
||||
watcher->deleteLater();
|
||||
|
||||
if (generation != scanGeneration) {
|
||||
|
|
@ -287,8 +322,10 @@ void VisualDeckStorageModel::beginLoad(int row)
|
|||
const int generation = scanGeneration;
|
||||
|
||||
auto *watcher = new QFutureWatcher<std::optional<DeckLoadResult>>(this);
|
||||
trackWatcher(watcher);
|
||||
connect(watcher, &QFutureWatcher<std::optional<DeckLoadResult>>::finished, this,
|
||||
[this, watcher, filePath, generation] {
|
||||
forgetWatcher(watcher);
|
||||
watcher->deleteLater();
|
||||
|
||||
if (generation != scanGeneration) {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@
|
|||
|
||||
#include <QAbstractListModel>
|
||||
#include <QDateTime>
|
||||
#include <QFutureWatcherBase>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QModelIndex>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
|
|
@ -107,6 +109,12 @@ class VisualDeckStorageModel : public QAbstractListModel
|
|||
public:
|
||||
explicit VisualDeckStorageModel(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Cancels and detaches every background scan/load still in flight, so no
|
||||
* QFutureWatcher can deliver a result into (or outlive) a destroyed model.
|
||||
*/
|
||||
~VisualDeckStorageModel() override;
|
||||
|
||||
/// @name Qt model overrides
|
||||
///@{
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
|
@ -177,6 +185,8 @@ private:
|
|||
static void recomputeDeckMetadata(DeckPreviewData &data, bool recomputeColorIdentity = true);
|
||||
void reindexFilePaths();
|
||||
void schedulePendingLoadDrain();
|
||||
void trackWatcher(QFutureWatcherBase *watcher);
|
||||
void forgetWatcher(QFutureWatcherBase *watcher);
|
||||
|
||||
private slots:
|
||||
void drainPendingLoads();
|
||||
|
|
@ -191,6 +201,7 @@ private:
|
|||
int scanGeneration = 0; ///< Bumped on every scan so stale results are ignored.
|
||||
QVector<PendingDeckLoad> pendingLoads; ///< Finished background loads waiting to be applied.
|
||||
bool drainScheduled = false; ///< Whether a queued drain pass is already pending.
|
||||
QList<QPointer<QFutureWatcherBase>> activeWatchers; ///< Scan/load watchers whose future has not finished yet.
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_MODEL_H
|
||||
|
|
|
|||
|
|
@ -157,6 +157,36 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
|
|||
}
|
||||
}
|
||||
|
||||
VisualDeckStorageWidget::~VisualDeckStorageWidget()
|
||||
{
|
||||
// Tear down in dependency order instead of relying on QObject child creation order,
|
||||
// which would destroy the model first while the proxy and the deck preview widgets
|
||||
// (which hold raw VisualDeckStorageModel pointers) are still alive.
|
||||
if (refreshTimer) {
|
||||
refreshTimer->stop();
|
||||
}
|
||||
|
||||
// 1. Views: folder and deck preview widgets live inside the scroll area.
|
||||
delete scrollArea;
|
||||
scrollArea = nullptr;
|
||||
folderWidget = nullptr;
|
||||
|
||||
// 2. Proxy: detach it from the model before either is destroyed.
|
||||
if (storageProxyModel) {
|
||||
disconnect(storageProxyModel, nullptr, this, nullptr);
|
||||
storageProxyModel->setSourceModel(nullptr);
|
||||
delete storageProxyModel;
|
||||
storageProxyModel = nullptr;
|
||||
}
|
||||
|
||||
// 3. Model last; its destructor cancels and detaches in-flight background loads.
|
||||
if (storageModel) {
|
||||
disconnect(storageModel, nullptr, this, nullptr);
|
||||
delete storageModel;
|
||||
storageModel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::refreshIfPossible()
|
||||
{
|
||||
if (scrollArea->widget() != databaseLoadIndicator) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class VisualDeckStorageWidget final : public QWidget
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit VisualDeckStorageWidget(QWidget *parent);
|
||||
~VisualDeckStorageWidget() override;
|
||||
void refreshIfPossible();
|
||||
void retranslateUi();
|
||||
void setShareSelectable(bool selectable);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue