Safely close database connections and delete backends

Also fix NewClosure leak caused by disconnected object signals
This commit is contained in:
Jonas Kvinge
2019-07-24 19:16:51 +02:00
parent bd78e8c275
commit b5eb13449b
47 changed files with 490 additions and 53 deletions

View File

@@ -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); }

View File

@@ -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<ApplicationImpl> p_;
QList<QThread*> threads_;
QList<QObject*> wait_for_exit_;
};

View File

@@ -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<quint64>(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<quint64>(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

View File

@@ -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;

View File

@@ -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_);

View File

@@ -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();
}

View File

@@ -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();