From b5eb13449b8b6b6f3ced459487f26e150561363f Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Wed, 24 Jul 2019 19:16:51 +0200 Subject: [PATCH] Safely close database connections and delete backends Also fix NewClosure leak caused by disconnected object signals --- ext/libstrawberry-common/core/closure.cpp | 6 ++- ext/libstrawberry-common/core/closure.h | 3 ++ src/collection/collection.cpp | 43 +++++++++++++++++--- src/collection/collection.h | 9 ++++ src/collection/collectionbackend.cpp | 36 +++++++++++++++- src/collection/collectionbackend.h | 12 +++++- src/collection/collectionmodel.cpp | 10 ++++- src/collection/collectionwatcher.cpp | 24 ++++++++++- src/collection/collectionwatcher.h | 6 +++ src/core/application.cpp | 39 +++++++++++++++++- src/core/application.h | 9 +++- src/core/database.cpp | 41 ++++++++++++++++++- src/core/database.h | 2 + src/core/deletefiles.cpp | 2 +- src/core/mainwindow.cpp | 15 ++++++- src/core/mainwindow.h | 3 ++ src/covermanager/currentalbumcoverloader.cpp | 5 ++- src/device/devicedatabasebackend.cpp | 40 +++++++++++++++++- src/device/devicedatabasebackend.h | 12 ++++++ src/device/devicemanager.cpp | 11 ++++- src/device/devicemanager.h | 3 ++ src/device/gpodloader.cpp | 2 + src/device/mtploader.cpp | 2 + src/internet/internetservice.h | 2 + src/internet/internetservices.cpp | 19 +++++++++ src/internet/internetservices.h | 8 ++++ src/musicbrainz/musicbrainzclient.cpp | 2 +- src/playlist/playlistbackend.cpp | 38 ++++++++++++++++- src/playlist/playlistbackend.h | 10 +++++ src/playlist/playlistdelegates.cpp | 5 +++ src/playlist/playlistview.h | 1 + src/qobuz/qobuzbaserequest.cpp | 2 +- src/qobuz/qobuzfavoriterequest.cpp | 2 +- src/qobuz/qobuzrequest.cpp | 2 +- src/qobuz/qobuzservice.cpp | 36 ++++++++++++---- src/qobuz/qobuzservice.h | 5 +++ src/qobuz/qobuzstreamurlrequest.cpp | 6 +-- src/subsonic/subsonicbaserequest.cpp | 2 +- src/subsonic/subsonicrequest.cpp | 2 +- src/subsonic/subsonicservice.cpp | 13 ++++-- src/subsonic/subsonicservice.h | 1 + src/tidal/tidalbaserequest.cpp | 2 +- src/tidal/tidalfavoriterequest.cpp | 2 +- src/tidal/tidalrequest.cpp | 2 +- src/tidal/tidalservice.cpp | 36 ++++++++++++---- src/tidal/tidalservice.h | 4 ++ src/tidal/tidalstreamurlrequest.cpp | 6 +-- 47 files changed, 490 insertions(+), 53 deletions(-) diff --git a/ext/libstrawberry-common/core/closure.cpp b/ext/libstrawberry-common/core/closure.cpp index ce88643b1..d1d157d46 100644 --- a/ext/libstrawberry-common/core/closure.cpp +++ b/ext/libstrawberry-common/core/closure.cpp @@ -22,6 +22,7 @@ #include "closure.h" +#include "core/logging.h" #include "core/timeconstants.h" namespace _detail { @@ -30,8 +31,7 @@ ClosureBase::ClosureBase(ObjectHelper *helper) : helper_(helper) { } -ClosureBase::~ClosureBase() { -} +ClosureBase::~ClosureBase() {} CallbackClosure::CallbackClosure(QObject *sender, const char *signal, std::function callback) : ClosureBase(new ObjectHelper(sender, signal, this)), @@ -53,6 +53,8 @@ ObjectHelper::ObjectHelper(QObject *sender, const char *signal, ClosureBase *clo } +ObjectHelper::~ObjectHelper() {} + void ObjectHelper::Invoked() { closure_->Invoke(); deleteLater(); diff --git a/ext/libstrawberry-common/core/closure.h b/ext/libstrawberry-common/core/closure.h index e7ff5eb31..6fa725b7f 100644 --- a/ext/libstrawberry-common/core/closure.h +++ b/ext/libstrawberry-common/core/closure.h @@ -34,6 +34,8 @@ #include #include +#include "core/logging.h" + namespace _detail { class ObjectHelper; @@ -62,6 +64,7 @@ class ObjectHelper : public QObject { Q_OBJECT public: ObjectHelper(QObject *parent, const char *signal, ClosureBase *closure); + ~ObjectHelper(); private slots: void Invoked(); diff --git a/src/collection/collection.cpp b/src/collection/collection.cpp index 727c7f072..86256eab9 100644 --- a/src/collection/collection.cpp +++ b/src/collection/collection.cpp @@ -21,6 +21,7 @@ #include "config.h" #include +#include #include #include @@ -50,7 +51,10 @@ SCollection::SCollection(Application *app, QObject *parent) backend_(nullptr), model_(nullptr), watcher_(nullptr), - watcher_thread_(nullptr) { + watcher_thread_(nullptr), + original_thread_(nullptr) { + + original_thread_ = thread(); backend_ = new CollectionBackend(); backend()->moveToThread(app->database()->thread()); @@ -64,11 +68,17 @@ SCollection::SCollection(Application *app, QObject *parent) } SCollection::~SCollection() { - watcher_->Stop(); - watcher_->deleteLater(); - watcher_thread_->exit(); - watcher_thread_->wait(5000 /* five seconds */); + + if (watcher_) { + watcher_->Stop(); + watcher_->deleteLater(); + } + if (watcher_thread_) { + watcher_thread_->exit(); + watcher_thread_->wait(5000 /* five seconds */); + } backend_->deleteLater(); + } void SCollection::Init() { @@ -98,6 +108,29 @@ void SCollection::Init() { // This will start the watcher checking for updates backend_->LoadDirectoriesAsync(); + +} + +void SCollection::Exit() { + + wait_for_exit_ << backend_ << watcher_; + + disconnect(backend_, 0, watcher_, 0); + disconnect(watcher_, 0, backend_, 0); + + connect(backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + connect(watcher_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + backend_->ExitAsync(); + watcher_->ExitAsync(); + +} + +void SCollection::ExitReceived() { + + disconnect(sender(), 0, this, 0); + wait_for_exit_.removeAll(sender()); + if (wait_for_exit_.isEmpty()) emit ExitFinished(); + } void SCollection::IncrementalScan() { watcher_->IncrementalScanAsync(); } diff --git a/src/collection/collection.h b/src/collection/collection.h index 54a0c695d..f370d7172 100644 --- a/src/collection/collection.h +++ b/src/collection/collection.h @@ -49,6 +49,7 @@ class SCollection : public QObject { static const char *kFtsTable; void Init(); + void Exit(); CollectionBackend *backend() const { return backend_; } CollectionModel *model() const { return model_; } @@ -70,12 +71,17 @@ class SCollection : public QObject { void Rescan(const SongList &songs); private slots: + void ExitReceived(); + void IncrementalScan(); void CurrentSongChanged(const Song &song); void SongsStatisticsChanged(const SongList& songs); void Stopped(); + signals: + void ExitFinished(); + private: Application *app_; CollectionBackend *backend_; @@ -83,9 +89,12 @@ class SCollection : public QObject { CollectionWatcher *watcher_; Thread *watcher_thread_; + QThread *original_thread_; // DB schema versions which should trigger a full collection rescan (each of those with a short reason why). QHash full_rescan_revisions_; + + QList wait_for_exit_; }; #endif diff --git a/src/collection/collectionbackend.cpp b/src/collection/collectionbackend.cpp index 6f127784a..1ceab777f 100644 --- a/src/collection/collectionbackend.cpp +++ b/src/collection/collectionbackend.cpp @@ -20,8 +20,12 @@ #include "config.h" +#include + #include #include +#include +#include #include #include #include @@ -52,7 +56,14 @@ const char *CollectionBackend::kSettingsGroup = "Collection"; CollectionBackend::CollectionBackend(QObject *parent) : CollectionBackendInterface(parent), - db_(nullptr) {} + db_(nullptr), + original_thread_(nullptr) { + + original_thread_ = thread(); + +} + +CollectionBackend::~CollectionBackend() {} void CollectionBackend::Init(Database *db, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table) { db_ = db; @@ -63,6 +74,29 @@ void CollectionBackend::Init(Database *db, const Song::Source source, const QStr fts_table_ = fts_table; } +void CollectionBackend::Close() { + + if (db_) { + QMutexLocker l(db_->Mutex()); + db_->Close(); + } + +} + +void CollectionBackend::ExitAsync() { + metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection); +} + +void CollectionBackend::Exit() { + + assert(QThread::currentThread() == thread()); + + Close(); + moveToThread(original_thread_); + emit ExitFinished(); + +} + void CollectionBackend::LoadDirectoriesAsync() { metaObject()->invokeMethod(this, "LoadDirectories", Qt::QueuedConnection); } diff --git a/src/collection/collectionbackend.h b/src/collection/collectionbackend.h index 58c35d1f5..33ffb81d4 100644 --- a/src/collection/collectionbackend.h +++ b/src/collection/collectionbackend.h @@ -41,6 +41,7 @@ #include "collectionquery.h" #include "directory.h" +class QThread; class Database; class CollectionBackendInterface : public QObject { @@ -123,7 +124,12 @@ class CollectionBackend : public CollectionBackendInterface { static const char *kSettingsGroup; Q_INVOKABLE CollectionBackend(QObject *parent = nullptr); + ~CollectionBackend(); + void Init(Database *db, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table); + void Close(); + + void ExitAsync(); Database *db() const { return db_; } @@ -183,6 +189,7 @@ class CollectionBackend : public CollectionBackendInterface { SongList GetSongsBySongId(const QStringList &song_ids); public slots: + void Exit(); void LoadDirectories(); void UpdateTotalSongCount(); void UpdateTotalArtistCount(); @@ -200,7 +207,7 @@ class CollectionBackend : public CollectionBackendInterface { void ResetStatistics(int id); void SongPathChanged(const Song &song, const QFileInfo &new_file); -signals: + signals: void DirectoryDiscovered(const Directory &dir, const SubdirectoryList &subdirs); void DirectoryDeleted(const Directory &dir); @@ -214,6 +221,8 @@ signals: void TotalArtistCountUpdated(int total); void TotalAlbumCountUpdated(int total); + void ExitFinished(); + private: struct CompilationInfo { CompilationInfo() : has_compilation_detected(false), has_not_compilation_detected(false) {} @@ -243,6 +252,7 @@ signals: QString dirs_table_; QString subdirs_table_; QString fts_table_; + QThread *original_thread_; }; diff --git a/src/collection/collectionmodel.cpp b/src/collection/collectionmodel.cpp index 483c9980a..cbc58ea54 100644 --- a/src/collection/collectionmodel.cpp +++ b/src/collection/collectionmodel.cpp @@ -129,7 +129,10 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q } -CollectionModel::~CollectionModel() { delete root_; } +CollectionModel::~CollectionModel() { + backend_->Close(); + delete root_; +} void CollectionModel::set_pretty_covers(bool use_pretty_covers) { @@ -757,6 +760,11 @@ CollectionModel::QueryResult CollectionModel::RunQuery(CollectionItem *parent) { while (q.Next()) { result.rows << SqlRow(q); } + + if (QThread::currentThread() != thread() && QThread::currentThread() != backend_->thread()) { + backend_->Close(); + } + return result; } diff --git a/src/collection/collectionwatcher.cpp b/src/collection/collectionwatcher.cpp index e167348df..8559f6a56 100644 --- a/src/collection/collectionwatcher.cpp +++ b/src/collection/collectionwatcher.cpp @@ -20,7 +20,11 @@ #include "config.h" +#include + #include +#include +#include #include #include #include @@ -79,7 +83,10 @@ CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent) rescan_timer_(new QTimer(this)), rescan_paused_(false), total_watches_(0), - cue_parser_(new CueParser(backend_, this)) { + cue_parser_(new CueParser(backend_, this)), + original_thread_(nullptr) { + + original_thread_ = thread(); rescan_timer_->setInterval(1000); rescan_timer_->setSingleShot(true); @@ -93,6 +100,21 @@ CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent) connect(rescan_timer_, SIGNAL(timeout()), SLOT(RescanPathsNow())); } +void CollectionWatcher::ExitAsync() { + metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection); +} + +void CollectionWatcher::Exit() { + + assert(QThread::currentThread() == thread()); + + Stop(); + if (backend_) backend_->Close(); + moveToThread(original_thread_); + emit ExitFinished(); + +} + CollectionWatcher::ScanTransaction::ScanTransaction(CollectionWatcher *watcher, const int dir, const bool incremental, const bool ignores_mtime, const bool prevent_delete) : progress_(0), progress_max_(0), diff --git a/src/collection/collectionwatcher.h b/src/collection/collectionwatcher.h index da399c5a5..940ee0334 100644 --- a/src/collection/collectionwatcher.h +++ b/src/collection/collectionwatcher.h @@ -60,6 +60,8 @@ class CollectionWatcher : public QObject { void Stop() { stop_requested_ = true; } + void ExitAsync(); + signals: void NewOrUpdatedSongs(const SongList &songs); void SongsMTimeUpdated(const SongList &songs); @@ -68,6 +70,7 @@ signals: void SubdirsDiscovered(const SubdirectoryList &subdirs); void SubdirsMTimeUpdated(const SubdirectoryList &subdirs); void CompilationsNeedUpdating(); + void ExitFinished(); void ScanStarted(int task_id); @@ -142,6 +145,7 @@ signals: }; private slots: + void Exit(); void DirectoryChanged(const QString &path); void IncrementalScanNow(); void FullScanNow(); @@ -204,6 +208,8 @@ signals: SongList song_rescan_queue_; // Set by ui thread + QThread *original_thread_; + }; inline QString CollectionWatcher::NoExtensionPart(const QString& fileName) { diff --git a/src/core/application.cpp b/src/core/application.cpp index a67992314..d52dc845d 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -223,10 +223,12 @@ Application::~Application() { for (QThread *thread : threads_) { thread->wait(); + thread->deleteLater(); } + } -void Application::MoveToNewThread(QObject *object) { +QThread *Application::MoveToNewThread(QObject *object) { QThread *thread = new QThread(this); @@ -234,6 +236,9 @@ void Application::MoveToNewThread(QObject *object) { thread->start(); threads_ << thread; + + return thread; + } void Application::MoveToThread(QObject *object, QThread *thread) { @@ -241,6 +246,38 @@ void Application::MoveToThread(QObject *object, QThread *thread) { object->moveToThread(thread); } +void Application::Exit() { + + wait_for_exit_ << collection() + << playlist_backend() + << device_manager() + << internet_services(); + + connect(collection(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + collection()->Exit(); + + connect(playlist_backend(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + playlist_backend()->ExitAsync(); + + connect(device_manager(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + device_manager()->Exit(); + + connect(internet_services(), SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + internet_services()->Exit(); + + database()->Close(); + +} + +void Application::ExitReceived() { + + disconnect(sender(), 0, this, 0); + + wait_for_exit_.removeAll(sender()); + if (wait_for_exit_.isEmpty()) emit ExitFinished(); + +} + void Application::AddError(const QString& message) { emit ErrorAdded(message); } void Application::ReloadSettings() { emit SettingsChanged(); } void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) { emit SettingsDialogRequested(page); } diff --git a/src/core/application.h b/src/core/application.h index 7bbb0ad13..dc9710928 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -111,9 +111,14 @@ class Application : public QObject { MoodbarLoader *moodbar_loader() const; #endif - void MoveToNewThread(QObject *object); + void Exit(); + + QThread *MoveToNewThread(QObject *object); void MoveToThread(QObject *object, QThread *thread); +private slots: + void ExitReceived(); + public slots: void AddError(const QString &message); void ReloadSettings(); @@ -123,10 +128,12 @@ signals: void ErrorAdded(const QString &message); void SettingsChanged(); void SettingsDialogRequested(SettingsDialog::Page page); + void ExitFinished(); private: std::unique_ptr p_; QList threads_; + QList wait_for_exit_; }; diff --git a/src/core/database.cpp b/src/core/database.cpp index 04daddbcf..1739f0a12 100644 --- a/src/core/database.cpp +++ b/src/core/database.cpp @@ -211,6 +211,7 @@ int Database::FTSNext(sqlite3_tokenizer_cursor *cursor, const char* *token, int void Database::StaticInit() { if (sFTSTokenizer) return; + sFTSTokenizer = new sqlite3_tokenizer_module; sFTSTokenizer->iVersion = 0; sFTSTokenizer->xCreate = &Database::FTSCreate; @@ -242,7 +243,21 @@ Database::Database(Application *app, QObject *parent, const QString &database_na } -Database::~Database() {} +Database::~Database() { + + QMutexLocker l(&connect_mutex_); + + for (QString connection : connections_) { + qLog(Error) << connection << "still open!"; + } + + if (!connections_.isEmpty()) + qLog(Error) << connections_.count() << "connections still open!"; + + if (sFTSTokenizer) + delete sFTSTokenizer; + +} QSqlDatabase Database::Connect() { @@ -257,6 +272,11 @@ QSqlDatabase Database::Connect() { const QString connection_id = QString("%1_thread_%2").arg(connection_id_).arg(reinterpret_cast(QThread::currentThread())); + if (!connections_.contains(connection_id)) { + //qLog(Debug) << "Opened database with connection id" << connection_id; + connections_ << connection_id; + } + // Try to find an existing connection for this thread QSqlDatabase db = QSqlDatabase::database(connection_id); if (db.isOpen()) { @@ -346,6 +366,25 @@ QSqlDatabase Database::Connect() { } +void Database::Close() { + + QMutexLocker l(&connect_mutex_); + + const QString connection_id = QString("%1_thread_%2").arg(connection_id_).arg(reinterpret_cast(QThread::currentThread())); + + // Try to find an existing connection for this thread + QSqlDatabase db = QSqlDatabase::database(connection_id); + if (db.isOpen()) { + db.close(); + } + + if (connections_.contains(connection_id)) { + //qLog(Debug) << "Closed database with connection id" << connection_id; + connections_.removeAll(connection_id); + } + +} + void Database::UpdateMainSchema(QSqlDatabase *db) { // Get the database's schema version diff --git a/src/core/database.h b/src/core/database.h index 05ab644e2..029420c4c 100644 --- a/src/core/database.h +++ b/src/core/database.h @@ -68,6 +68,7 @@ class Database : public QObject { static const char *kMagicAllSongsTables; QSqlDatabase Connect(); + void Close(); bool CheckErrors(const QSqlQuery &query); QMutex *Mutex() { return &mutex_; } @@ -111,6 +112,7 @@ signals: // This ID makes the QSqlDatabase name unique to the object as well as the thread int connection_id_; + QStringList connections_; static QMutex sNextConnectionIdMutex; static int sNextConnectionId; diff --git a/src/core/deletefiles.cpp b/src/core/deletefiles.cpp index aa040bdf0..f4c2e8f8d 100644 --- a/src/core/deletefiles.cpp +++ b/src/core/deletefiles.cpp @@ -55,7 +55,7 @@ void DeleteFiles::Start(const SongList &songs) { task_id_ = task_manager_->StartTask(tr("Deleting files")); task_manager_->SetTaskBlocksCollectionScans(true); - thread_ = new QThread; + thread_ = new QThread(this); connect(thread_, SIGNAL(started()), SLOT(ProcessSomeFiles())); moveToThread(thread_); diff --git a/src/core/mainwindow.cpp b/src/core/mainwindow.cpp index 834603eee..b22b03b00 100644 --- a/src/core/mainwindow.cpp +++ b/src/core/mainwindow.cpp @@ -989,7 +989,7 @@ void MainWindow::Exit() { if (app_->player()->engine()->is_fadeout_enabled()) { // To shut down the application when fadeout will be finished - connect(app_->player()->engine(), SIGNAL(FadeoutFinishedSignal()), qApp, SLOT(quit())); + connect(app_->player()->engine(), SIGNAL(FadeoutFinishedSignal()), this, SLOT(DoExit())); if (app_->player()->GetState() == Engine::Playing) { app_->player()->Stop(); hide(); @@ -998,6 +998,19 @@ void MainWindow::Exit() { } } + DoExit(); + +} + +void MainWindow::DoExit() { + + connect(app_, SIGNAL(ExitFinished()), this, SLOT(ExitFinished())); + app_->Exit(); + +} + +void MainWindow::ExitFinished() { + qApp->quit(); } diff --git a/src/core/mainwindow.h b/src/core/mainwindow.h index 2cf722556..37f2cfb52 100644 --- a/src/core/mainwindow.h +++ b/src/core/mainwindow.h @@ -243,6 +243,7 @@ signals: void Raise(); void Exit(); + void DoExit(); void HandleNotificationPreview(OSD::Behaviour type, QString line1, QString line2); @@ -262,6 +263,8 @@ signals: void LoveButtonVisibilityChanged(bool value); void Love(); + void ExitFinished(); + private: void SaveSettings(); diff --git a/src/covermanager/currentalbumcoverloader.cpp b/src/covermanager/currentalbumcoverloader.cpp index 08a64a68a..1c2783d1b 100644 --- a/src/covermanager/currentalbumcoverloader.cpp +++ b/src/covermanager/currentalbumcoverloader.cpp @@ -52,7 +52,10 @@ CurrentAlbumCoverLoader::CurrentAlbumCoverLoader(Application *app, QObject *pare } -CurrentAlbumCoverLoader::~CurrentAlbumCoverLoader() {} +CurrentAlbumCoverLoader::~CurrentAlbumCoverLoader() { + if (temp_cover_) temp_cover_->remove(); + if (temp_cover_thumbnail_) temp_cover_thumbnail_->remove(); +} void CurrentAlbumCoverLoader::LoadAlbumCover(const Song &song) { last_song_ = song; diff --git a/src/device/devicedatabasebackend.cpp b/src/device/devicedatabasebackend.cpp index 75abe2800..d28ab9dca 100644 --- a/src/device/devicedatabasebackend.cpp +++ b/src/device/devicedatabasebackend.cpp @@ -18,9 +18,14 @@ * */ +#include "config.h" + #include +#include #include +#include +#include #include #include #include @@ -32,17 +37,47 @@ #include "core/database.h" #include "core/scopedtransaction.h" +#include "core/logging.h" #include "devicedatabasebackend.h" const int DeviceDatabaseBackend::kDeviceSchemaVersion = 0; DeviceDatabaseBackend::DeviceDatabaseBackend(QObject *parent) : QObject(parent), - db_(nullptr) - {} + db_(nullptr), + original_thread_(nullptr) + { + + original_thread_ = thread(); + +} + +DeviceDatabaseBackend::~DeviceDatabaseBackend() {} void DeviceDatabaseBackend::Init(Database* db) { db_ = db; } +void DeviceDatabaseBackend::Close() { + + if (db_) { + QMutexLocker l(db_->Mutex()); + db_->Close(); + } + +} + +void DeviceDatabaseBackend::ExitAsync() { + metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection); +} + +void DeviceDatabaseBackend::Exit() { + + assert(QThread::currentThread() == thread()); + Close(); + moveToThread(original_thread_); + emit ExitFinished(); + +} + DeviceDatabaseBackend::DeviceList DeviceDatabaseBackend::GetAllDevices() { QMutexLocker l(db_->Mutex()); @@ -101,6 +136,7 @@ int DeviceDatabaseBackend::AddDevice(const Device &device) { db_->ExecSchemaCommands(db, schema, 0, true); t.Commit(); + return id; } diff --git a/src/device/devicedatabasebackend.h b/src/device/devicedatabasebackend.h index 11bfd0748..ab3e355c7 100644 --- a/src/device/devicedatabasebackend.h +++ b/src/device/devicedatabasebackend.h @@ -40,6 +40,7 @@ class DeviceDatabaseBackend : public QObject { public: Q_INVOKABLE DeviceDatabaseBackend(QObject *parent = nullptr); + ~DeviceDatabaseBackend(); struct Device { Device() : id_(-1) {} @@ -58,6 +59,9 @@ class DeviceDatabaseBackend : public QObject { static const int kDeviceSchemaVersion; void Init(Database *db); + void Close(); + void ExitAsync(); + Database *db() const { return db_; } DeviceList GetAllDevices(); @@ -66,8 +70,16 @@ class DeviceDatabaseBackend : public QObject { void SetDeviceOptions(int id, const QString &friendly_name, const QString &icon_name, MusicStorage::TranscodeMode mode, Song::FileType format); + private slots: + void Exit(); + + signals: + void ExitFinished(); + private: Database *db_; + QThread *original_thread_; + }; #endif // DEVICEDATABASEBACKEND_H diff --git a/src/device/devicemanager.cpp b/src/device/devicemanager.cpp index b23f12e0c..0b0c0aa2f 100644 --- a/src/device/devicemanager.cpp +++ b/src/device/devicemanager.cpp @@ -158,9 +158,17 @@ DeviceManager::~DeviceManager() { delete lister; } - backend_->deleteLater(); + backend_->Close(); delete root_; + delete backend_; + +} + +void DeviceManager::Exit() { + + connect(backend_, SIGNAL(ExitFinished()), this, SIGNAL(ExitFinished())); + backend_->ExitAsync(); } @@ -174,6 +182,7 @@ void DeviceManager::LoadAllDevices() { info->InitFromDb(device); emit DeviceCreatedFromDB(info); } + backend_->Close(); } diff --git a/src/device/devicemanager.h b/src/device/devicemanager.h index 3e4abd213..d916dfe5f 100644 --- a/src/device/devicemanager.h +++ b/src/device/devicemanager.h @@ -85,6 +85,8 @@ class DeviceManager : public SimpleTreeModel { static const int kDeviceIconSize; static const int kDeviceIconOverlaySize; + void Exit(); + DeviceStateFilterModel *connected_devices_model() const { return connected_devices_model_; } // Get info about devices @@ -115,6 +117,7 @@ class DeviceManager : public SimpleTreeModel { void Unmount(QModelIndex idx); signals: + void ExitFinished(); void DeviceConnected(QModelIndex idx); void DeviceDisconnected(QModelIndex idx); void DeviceCreatedFromDB(DeviceInfo* info); diff --git a/src/device/gpodloader.cpp b/src/device/gpodloader.cpp index 8b9803022..8aa2a4bae 100644 --- a/src/device/gpodloader.cpp +++ b/src/device/gpodloader.cpp @@ -102,6 +102,8 @@ Itdb_iTunesDB *GPodLoader::TryLoad() { // Add the songs we've just loaded backend_->AddOrUpdateSongs(songs); + backend_->Close(); + return db; } diff --git a/src/device/mtploader.cpp b/src/device/mtploader.cpp index 9e55f70f3..40c7112a1 100644 --- a/src/device/mtploader.cpp +++ b/src/device/mtploader.cpp @@ -96,6 +96,8 @@ bool MtpLoader::TryLoad() { // Add the songs we've just loaded backend_->AddOrUpdateSongs(songs); + backend_->Close(); + return true; } diff --git a/src/internet/internetservice.h b/src/internet/internetservice.h index 794e43fda..f7928519d 100644 --- a/src/internet/internetservice.h +++ b/src/internet/internetservice.h @@ -40,6 +40,7 @@ class InternetService : public QObject { InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, QObject *parent = nullptr); virtual ~InternetService() {} + virtual void Exit() {} virtual Song::Source source() const { return source_; } virtual QString name() const { return name_; } @@ -75,6 +76,7 @@ class InternetService : public QObject { virtual void ResetSongsRequest() {} signals: + void ExitFinished(); void Login(); void Logout(); void Login(const QString &api_token, const QString &username, const QString &password); diff --git a/src/internet/internetservices.cpp b/src/internet/internetservices.cpp index b86927747..1938c23ff 100644 --- a/src/internet/internetservices.cpp +++ b/src/internet/internetservices.cpp @@ -72,3 +72,22 @@ void InternetServices::ReloadSettings() { service->ReloadSettings(); } } + +void InternetServices::Exit() { + + for (InternetService *service : services_.values()) { + wait_for_exit_ << service; + connect(service, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + service->Exit(); + } + +} + +void InternetServices::ExitReceived() { + + InternetService *service = qobject_cast(sender()); + + wait_for_exit_.removeAll(service); + if (wait_for_exit_.isEmpty()) emit ExitFinished(); + +} diff --git a/src/internet/internetservices.h b/src/internet/internetservices.h index 283799abb..20927f99b 100644 --- a/src/internet/internetservices.h +++ b/src/internet/internetservices.h @@ -47,9 +47,17 @@ class InternetServices : public QObject { void AddService(InternetService *service); void RemoveService(InternetService *service); void ReloadSettings(); + void Exit(); + + signals: + void ExitFinished(); + + private slots: + void ExitReceived(); private: QMap services_; + QList wait_for_exit_; }; diff --git a/src/musicbrainz/musicbrainzclient.cpp b/src/musicbrainz/musicbrainzclient.cpp index a194648e2..dcbbfccfa 100644 --- a/src/musicbrainz/musicbrainzclient.cpp +++ b/src/musicbrainz/musicbrainzclient.cpp @@ -115,7 +115,7 @@ void MusicBrainzClient::Cancel(int id) { while (!requests_.isEmpty() && requests_.contains(id)) { QNetworkReply *reply = requests_.take(id); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); if (reply->isRunning()) reply->abort(); reply->deleteLater(); } diff --git a/src/playlist/playlistbackend.cpp b/src/playlist/playlistbackend.cpp index 832e6d324..07fdd2f5f 100644 --- a/src/playlist/playlistbackend.cpp +++ b/src/playlist/playlistbackend.cpp @@ -20,8 +20,11 @@ #include #include +#include #include +#include +#include #include #include #include @@ -56,7 +59,36 @@ using std::shared_ptr; const int PlaylistBackend::kSongTableJoins = 2; PlaylistBackend::PlaylistBackend(Application *app, QObject *parent) - : QObject(parent), app_(app), db_(app_->database()) {} + : QObject(parent), app_(app), db_(app_->database()), original_thread_(nullptr) { + + original_thread_ = thread(); + +} + +PlaylistBackend::~PlaylistBackend() {} + +void PlaylistBackend::Close() { + + if (db_) { + QMutexLocker l(db_->Mutex()); + db_->Close(); + } + +} + +void PlaylistBackend::ExitAsync() { + metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection); +} + +void PlaylistBackend::Exit() { + + assert(QThread::currentThread() == thread()); + + Close(); + moveToThread(original_thread_); + emit ExitFinished(); + +} PlaylistBackend::PlaylistList PlaylistBackend::GetAllPlaylists() { return GetPlaylists(GetPlaylists_All); @@ -157,6 +189,10 @@ QSqlQuery PlaylistBackend::GetPlaylistRows(int playlist) { q.bindValue(":playlist", playlist); q.exec(); + if (QThread::currentThread() != thread() && QThread::currentThread() != qApp->thread()) { + db_->Close(); + } + return q; } diff --git a/src/playlist/playlistbackend.h b/src/playlist/playlistbackend.h index f00096dc5..7f09719e3 100644 --- a/src/playlist/playlistbackend.h +++ b/src/playlist/playlistbackend.h @@ -39,6 +39,7 @@ #include "collection/sqlrow.h" #include "playlistitem.h" +class QThread; class Application; class Database; @@ -47,6 +48,7 @@ class PlaylistBackend : public QObject { public: Q_INVOKABLE PlaylistBackend(Application *app, QObject *parent = nullptr); + ~PlaylistBackend(); struct Playlist { Playlist() : id(-1), favorite(false), last_played(0) {} @@ -62,6 +64,9 @@ class PlaylistBackend : public QObject { static const int kSongTableJoins; + void Close(); + void ExitAsync(); + PlaylistList GetAllPlaylists(); PlaylistList GetAllOpenPlaylists(); PlaylistList GetAllFavoritePlaylists(); @@ -82,8 +87,12 @@ class PlaylistBackend : public QObject { Application *app() const { return app_; } public slots: + void Exit(); void SavePlaylist(int playlist, const PlaylistItemList &items, int last_played); +signals: + void ExitFinished(); + private: struct NewSongFromQueryState { QHash cached_cues_; @@ -105,6 +114,7 @@ class PlaylistBackend : public QObject { Application *app_; Database *db_; + QThread *original_thread_; }; #endif // PLAYLISTBACKEND_H diff --git a/src/playlist/playlistdelegates.cpp b/src/playlist/playlistdelegates.cpp index 227a74069..620b92c22 100644 --- a/src/playlist/playlistdelegates.cpp +++ b/src/playlist/playlistdelegates.cpp @@ -22,6 +22,7 @@ #include "config.h" #include +#include #include #include #include @@ -363,6 +364,10 @@ TagCompletionModel::TagCompletionModel(CollectionBackend *backend, Playlist::Col setStringList(backend->GetAll(col)); } + if (QThread::currentThread() != backend->thread() && QThread::currentThread() != qApp->thread()) { + backend->Close(); + } + } QString TagCompletionModel::database_column(Playlist::Column column) { diff --git a/src/playlist/playlistview.h b/src/playlist/playlistview.h index f3b72d1e6..a93bf61d0 100644 --- a/src/playlist/playlistview.h +++ b/src/playlist/playlistview.h @@ -80,6 +80,7 @@ class PlaylistHeader; class PlaylistProxyStyle : public QProxyStyle { public: PlaylistProxyStyle(QStyle *base); + void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const; void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const; diff --git a/src/qobuz/qobuzbaserequest.cpp b/src/qobuz/qobuzbaserequest.cpp index 9ea22491c..6983ce7b2 100644 --- a/src/qobuz/qobuzbaserequest.cpp +++ b/src/qobuz/qobuzbaserequest.cpp @@ -51,7 +51,7 @@ QobuzBaseRequest::~QobuzBaseRequest() { while (!replies_.isEmpty()) { QNetworkReply *reply = replies_.takeFirst(); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); if (reply->isRunning()) reply->abort(); reply->deleteLater(); } diff --git a/src/qobuz/qobuzfavoriterequest.cpp b/src/qobuz/qobuzfavoriterequest.cpp index 72c69e3da..77e0ed21d 100644 --- a/src/qobuz/qobuzfavoriterequest.cpp +++ b/src/qobuz/qobuzfavoriterequest.cpp @@ -49,7 +49,7 @@ QobuzFavoriteRequest::~QobuzFavoriteRequest() { while (!replies_.isEmpty()) { QNetworkReply *reply = replies_.takeFirst(); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); reply->abort(); reply->deleteLater(); } diff --git a/src/qobuz/qobuzrequest.cpp b/src/qobuz/qobuzrequest.cpp index cfb13a03a..1f1faf4c4 100644 --- a/src/qobuz/qobuzrequest.cpp +++ b/src/qobuz/qobuzrequest.cpp @@ -77,7 +77,7 @@ QobuzRequest::~QobuzRequest() { while (!album_cover_replies_.isEmpty()) { QNetworkReply *reply = album_cover_replies_.takeFirst(); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); if (reply->isRunning()) reply->abort(); reply->deleteLater(); } diff --git a/src/qobuz/qobuzservice.cpp b/src/qobuz/qobuzservice.cpp index 874c1586c..878aa9951 100644 --- a/src/qobuz/qobuzservice.cpp +++ b/src/qobuz/qobuzservice.cpp @@ -176,13 +176,35 @@ QobuzService::~QobuzService() { while (!stream_url_requests_.isEmpty()) { QobuzStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst(); - disconnect(stream_url_req, 0, nullptr, 0); + disconnect(stream_url_req, 0, this, 0); stream_url_req->deleteLater(); } - artists_collection_backend_->deleteLater(); - albums_collection_backend_->deleteLater(); - songs_collection_backend_->deleteLater(); + delete artists_collection_backend_; + delete albums_collection_backend_; + delete songs_collection_backend_; + +} + +void QobuzService::Exit() { + + wait_for_exit_ << artists_collection_backend_ << albums_collection_backend_ << songs_collection_backend_; + + connect(artists_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + connect(albums_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + connect(songs_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + + artists_collection_backend_->ExitAsync(); + albums_collection_backend_->ExitAsync(); + songs_collection_backend_->ExitAsync(); + +} + +void QobuzService::ExitReceived() { + + disconnect(sender(), 0, this, 0); + wait_for_exit_.removeAll(sender()); + if (wait_for_exit_.isEmpty()) emit ExitFinished(); } @@ -395,7 +417,7 @@ void QobuzService::TryLogin() { void QobuzService::ResetArtistsRequest() { if (artists_request_.get()) { - disconnect(artists_request_.get(), 0, nullptr, 0); + disconnect(artists_request_.get(), 0, this, 0); disconnect(this, 0, artists_request_.get(), 0); artists_request_.reset(); } @@ -446,7 +468,7 @@ void QobuzService::ArtistsUpdateProgressReceived(const int id, const int progres void QobuzService::ResetAlbumsRequest() { if (albums_request_.get()) { - disconnect(albums_request_.get(), 0, nullptr, 0); + disconnect(albums_request_.get(), 0, this, 0); disconnect(this, 0, albums_request_.get(), 0); albums_request_.reset(); } @@ -495,7 +517,7 @@ void QobuzService::AlbumsUpdateProgressReceived(const int id, const int progress void QobuzService::ResetSongsRequest() { if (songs_request_.get()) { - disconnect(songs_request_.get(), 0, nullptr, 0); + disconnect(songs_request_.get(), 0, this, 0); disconnect(this, 0, songs_request_.get(), 0); songs_request_.reset(); } diff --git a/src/qobuz/qobuzservice.h b/src/qobuz/qobuzservice.h index 3ad296283..5aae20bbc 100644 --- a/src/qobuz/qobuzservice.h +++ b/src/qobuz/qobuzservice.h @@ -40,6 +40,7 @@ #include "internet/internetsearch.h" #include "settings/qobuzsettingspage.h" +class QThread; class Application; class NetworkAccessManager; class QobuzUrlHandler; @@ -60,6 +61,7 @@ class QobuzService : public InternetService { static const Song::Source kSource; + void Exit(); void ReloadSettings(); void Logout(); @@ -123,6 +125,7 @@ class QobuzService : public InternetService { void ResetSongsRequest(); private slots: + void ExitReceived(); void SendLogin(); void HandleLoginSSLErrors(QList ssl_errors); void HandleAuthReply(QNetworkReply *reply); @@ -217,6 +220,8 @@ class QobuzService : public InternetService { QStringList login_errors_; + QList wait_for_exit_; + }; #endif // QOBUZSERVICE_H diff --git a/src/qobuz/qobuzstreamurlrequest.cpp b/src/qobuz/qobuzstreamurlrequest.cpp index 0e25f59de..d9d036b70 100644 --- a/src/qobuz/qobuzstreamurlrequest.cpp +++ b/src/qobuz/qobuzstreamurlrequest.cpp @@ -54,7 +54,7 @@ QobuzStreamURLRequest::QobuzStreamURLRequest(QobuzService *service, NetworkAcces QobuzStreamURLRequest::~QobuzStreamURLRequest() { if (reply_) { - disconnect(reply_, 0, nullptr, 0); + disconnect(reply_, 0, this, 0); if (reply_->isRunning()) reply_->abort(); reply_->deleteLater(); } @@ -107,7 +107,7 @@ void QobuzStreamURLRequest::GetStreamURL() { ++tries_; if (reply_) { - disconnect(reply_, 0, nullptr, 0); + disconnect(reply_, 0, this, 0); if (reply_->isRunning()) reply_->abort(); reply_->deleteLater(); } @@ -147,7 +147,7 @@ void QobuzStreamURLRequest::GetStreamURL() { void QobuzStreamURLRequest::StreamURLReceived() { if (!reply_) return; - disconnect(reply_, 0, nullptr, 0); + disconnect(reply_, 0, this, 0); reply_->deleteLater(); QByteArray data = GetReplyData(reply_); diff --git a/src/subsonic/subsonicbaserequest.cpp b/src/subsonic/subsonicbaserequest.cpp index 1d750ef8f..f3f52a938 100644 --- a/src/subsonic/subsonicbaserequest.cpp +++ b/src/subsonic/subsonicbaserequest.cpp @@ -50,7 +50,7 @@ SubsonicBaseRequest::~SubsonicBaseRequest() { while (!replies_.isEmpty()) { QNetworkReply *reply = replies_.takeFirst(); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); if (reply->isRunning()) reply->abort(); reply->deleteLater(); } diff --git a/src/subsonic/subsonicrequest.cpp b/src/subsonic/subsonicrequest.cpp index c30093471..b3e8cea9b 100644 --- a/src/subsonic/subsonicrequest.cpp +++ b/src/subsonic/subsonicrequest.cpp @@ -71,7 +71,7 @@ SubsonicRequest::~SubsonicRequest() { while (!album_cover_replies_.isEmpty()) { QNetworkReply *reply = album_cover_replies_.takeFirst(); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); if (reply->isRunning()) reply->abort(); reply->deleteLater(); } diff --git a/src/subsonic/subsonicservice.cpp b/src/subsonic/subsonicservice.cpp index e0b685577..71e306ff3 100644 --- a/src/subsonic/subsonicservice.cpp +++ b/src/subsonic/subsonicservice.cpp @@ -95,7 +95,14 @@ SubsonicService::SubsonicService(Application *app, QObject *parent) } SubsonicService::~SubsonicService() { - collection_backend_->deleteLater(); + delete collection_backend_; +} + +void SubsonicService::Exit() { + + connect(collection_backend_, SIGNAL(ExitFinished()), this, SIGNAL(ExitFinished())); + collection_backend_->ExitAsync(); + } void SubsonicService::ShowConfig() { @@ -317,8 +324,8 @@ void SubsonicService::CheckConfiguration() { void SubsonicService::ResetSongsRequest() { - if (songs_request_.get()) { - disconnect(songs_request_.get(), 0, nullptr, 0); + if (songs_request_.get()) { // WARNING: Don't disconnect everything. NewClosure() relies on destroyed()!!! + disconnect(songs_request_.get(), 0, this, 0); disconnect(this, 0, songs_request_.get(), 0); songs_request_.reset(); } diff --git a/src/subsonic/subsonicservice.h b/src/subsonic/subsonicservice.h index 755fef6f7..f449a66c1 100644 --- a/src/subsonic/subsonicservice.h +++ b/src/subsonic/subsonicservice.h @@ -60,6 +60,7 @@ class SubsonicService : public InternetService { static const Song::Source kSource; void ReloadSettings(); + void Exit(); Application *app() { return app_; } diff --git a/src/tidal/tidalbaserequest.cpp b/src/tidal/tidalbaserequest.cpp index 959722476..236012fec 100644 --- a/src/tidal/tidalbaserequest.cpp +++ b/src/tidal/tidalbaserequest.cpp @@ -52,7 +52,7 @@ TidalBaseRequest::~TidalBaseRequest() { while (!replies_.isEmpty()) { QNetworkReply *reply = replies_.takeFirst(); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); if (reply->isRunning()) reply->abort(); reply->deleteLater(); } diff --git a/src/tidal/tidalfavoriterequest.cpp b/src/tidal/tidalfavoriterequest.cpp index b9a89baeb..7af953395 100644 --- a/src/tidal/tidalfavoriterequest.cpp +++ b/src/tidal/tidalfavoriterequest.cpp @@ -50,7 +50,7 @@ TidalFavoriteRequest::~TidalFavoriteRequest() { while (!replies_.isEmpty()) { QNetworkReply *reply = replies_.takeFirst(); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); reply->abort(); reply->deleteLater(); } diff --git a/src/tidal/tidalrequest.cpp b/src/tidal/tidalrequest.cpp index 6ad1816a1..bffbe6f1c 100644 --- a/src/tidal/tidalrequest.cpp +++ b/src/tidal/tidalrequest.cpp @@ -80,7 +80,7 @@ TidalRequest::~TidalRequest() { while (!album_cover_replies_.isEmpty()) { QNetworkReply *reply = album_cover_replies_.takeFirst(); - disconnect(reply, 0, nullptr, 0); + disconnect(reply, 0, this, 0); if (reply->isRunning()) reply->abort(); reply->deleteLater(); } diff --git a/src/tidal/tidalservice.cpp b/src/tidal/tidalservice.cpp index de4d87960..4afe00baf 100644 --- a/src/tidal/tidalservice.cpp +++ b/src/tidal/tidalservice.cpp @@ -182,13 +182,35 @@ TidalService::~TidalService() { while (!stream_url_requests_.isEmpty()) { TidalStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst(); - disconnect(stream_url_req, 0, nullptr, 0); + disconnect(stream_url_req, 0, this, 0); stream_url_req->deleteLater(); } - artists_collection_backend_->deleteLater(); - albums_collection_backend_->deleteLater(); - songs_collection_backend_->deleteLater(); + delete artists_collection_backend_; + delete albums_collection_backend_; + delete songs_collection_backend_; + +} + +void TidalService::Exit() { + + wait_for_exit_ << artists_collection_backend_ << albums_collection_backend_ << songs_collection_backend_; + + connect(artists_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + connect(albums_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + connect(songs_collection_backend_, SIGNAL(ExitFinished()), this, SLOT(ExitReceived())); + + artists_collection_backend_->ExitAsync(); + albums_collection_backend_->ExitAsync(); + songs_collection_backend_->ExitAsync(); + +} + +void TidalService::ExitReceived() { + + disconnect(sender(), 0, this, 0); + wait_for_exit_.removeAll(sender()); + if (wait_for_exit_.isEmpty()) emit ExitFinished(); } @@ -639,7 +661,7 @@ void TidalService::TryLogin() { void TidalService::ResetArtistsRequest() { if (artists_request_.get()) { - disconnect(artists_request_.get(), 0, nullptr, 0); + disconnect(artists_request_.get(), 0, this, 0); disconnect(this, 0, artists_request_.get(), 0); artists_request_.reset(); } @@ -694,7 +716,7 @@ void TidalService::ArtistsUpdateProgressReceived(const int id, const int progres void TidalService::ResetAlbumsRequest() { if (albums_request_.get()) { - disconnect(albums_request_.get(), 0, nullptr, 0); + disconnect(albums_request_.get(), 0, this, 0); disconnect(this, 0, albums_request_.get(), 0); albums_request_.reset(); } @@ -747,7 +769,7 @@ void TidalService::AlbumsUpdateProgressReceived(const int id, const int progress void TidalService::ResetSongsRequest() { if (songs_request_.get()) { - disconnect(songs_request_.get(), 0, nullptr, 0); + disconnect(songs_request_.get(), 0, this, 0); disconnect(this, 0, songs_request_.get(), 0); songs_request_.reset(); } diff --git a/src/tidal/tidalservice.h b/src/tidal/tidalservice.h index 1028c0c23..3e6962351 100644 --- a/src/tidal/tidalservice.h +++ b/src/tidal/tidalservice.h @@ -61,6 +61,7 @@ class TidalService : public InternetService { static const Song::Source kSource; + void Exit(); void ReloadSettings(); void Logout(); @@ -132,6 +133,7 @@ class TidalService : public InternetService { void ResetSongsRequest(); private slots: + void ExitReceived(); void StartAuthorisation(); void AuthorisationUrlReceived(const QUrl &url); void HandleLoginSSLErrors(QList ssl_errors); @@ -245,6 +247,8 @@ class TidalService : public InternetService { QStringList login_errors_; + QList wait_for_exit_; + }; #endif // TIDALSERVICE_H diff --git a/src/tidal/tidalstreamurlrequest.cpp b/src/tidal/tidalstreamurlrequest.cpp index 22a1fb60d..7eb6a4008 100644 --- a/src/tidal/tidalstreamurlrequest.cpp +++ b/src/tidal/tidalstreamurlrequest.cpp @@ -52,7 +52,7 @@ TidalStreamURLRequest::TidalStreamURLRequest(TidalService *service, NetworkAcces TidalStreamURLRequest::~TidalStreamURLRequest() { if (reply_) { - disconnect(reply_, 0, nullptr, 0); + disconnect(reply_, 0, this, 0); if (reply_->isRunning()) reply_->abort(); reply_->deleteLater(); } @@ -109,7 +109,7 @@ void TidalStreamURLRequest::GetStreamURL() { ++tries_; if (reply_) { - disconnect(reply_, 0, nullptr, 0); + disconnect(reply_, 0, this, 0); if (reply_->isRunning()) reply_->abort(); reply_->deleteLater(); } @@ -144,7 +144,7 @@ void TidalStreamURLRequest::GetStreamURL() { void TidalStreamURLRequest::StreamURLReceived() { if (!reply_) return; - disconnect(reply_, 0, nullptr, 0); + disconnect(reply_, 0, this, 0); reply_->deleteLater(); QByteArray data = GetReplyData(reply_, true);