From 40d3a3629637e588b04f5c207f5d9a30fcac8709 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 8 Apr 2021 14:56:06 -0400 Subject: [PATCH] Bug2700: intermittent failure to open project database... (#799) ... Logging data from the wild shows that the SELECT query in ProjectFileIO::CheckVersion() returned SQLITE_BUSY sometimes. A plausible explanation is that the concurrently starting checkpoint thread was sometimes creating a new connection simultaneously. Instead, serialize the creation of the two connections in the main thread and pass the second one ready made into the checkpoint thread. --- src/DBConnection.cpp | 41 ++++++++++++++++++++++++----------------- src/DBConnection.h | 2 +- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/DBConnection.cpp b/src/DBConnection.cpp index 746800215..5382e91f5 100644 --- a/src/DBConnection.cpp +++ b/src/DBConnection.cpp @@ -155,7 +155,20 @@ int DBConnection::Open(const FilePath fileName) mCheckpointStop = false; mCheckpointPending = false; mCheckpointActive = false; - mCheckpointThread = std::thread([this]{ CheckpointThread(); }); + + // Open another connection to the DB to prevent blocking the main thread. + // + // If it fails, then we won't checkpoint until the main thread closes + // the associated DB. + sqlite3 *db = nullptr; + const auto name = sqlite3_db_filename(mDB, nullptr); + if (sqlite3_open(name, &db) == SQLITE_OK && + // Configure it to be safe + ModeConfig(db, "main", SafeConfig) ) { + mCheckpointThread = std::thread([this, db]{ CheckpointThread(db); }); + } + else + sqlite3_close(db); // Install our checkpoint hook sqlite3_wal_hook(mDB, CheckpointHook, this); @@ -213,7 +226,8 @@ bool DBConnection::Close() } // And wait for it to do so - mCheckpointThread.join(); + if (mCheckpointThread.joinable()) + mCheckpointThread.join(); // We're done with the prepared statements { @@ -373,22 +387,14 @@ sqlite3_stmt *DBConnection::Prepare(enum StatementID id, const char *sql) return stmt; } -void DBConnection::CheckpointThread() +void DBConnection::CheckpointThread(sqlite3 *db) { - // Open another connection to the DB to prevent blocking the main thread. - // - // If it fails, then we won't checkpoint until the main thread closes - // the associated DB. - sqlite3 *db = nullptr; - const auto name = sqlite3_db_filename(mDB, nullptr); + int rc = SQLITE_OK; bool giveUp = false; - - auto rc = sqlite3_open(name, &db); - if (rc == SQLITE_OK) + const char *name = nullptr; + if (db) { - // Configure it to be safe - ModeConfig(db, "main", SafeConfig); - + name = sqlite3_db_filename(db, nullptr); while (true) { { @@ -484,8 +490,9 @@ void DBConnection::CheckpointThread() } // All done (always close) - rc = sqlite3_close(db); - if (rc != SQLITE_OK) + if (db) + rc = sqlite3_close(db); + if (rc != SQLITE_OK && name) { wxLogMessage("Checkpoint thread failed to close %s\n" "\tErrCode: %d\n" diff --git a/src/DBConnection.h b/src/DBConnection.h index 821e00df3..1ee61fd0e 100644 --- a/src/DBConnection.h +++ b/src/DBConnection.h @@ -98,7 +98,7 @@ public: private: bool ModeConfig(sqlite3 *db, const char *schema, const char *config); - void CheckpointThread(); + void CheckpointThread(sqlite3 *db); static int CheckpointHook(void *data, sqlite3 *db, const char *schema, int pages); private: