Safely close database connections and delete backends
Also fix NewClosure leak caused by disconnected object signals
This commit is contained in:
@@ -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); }
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user