1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-23 09:20:16 +01:00

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.
This commit is contained in:
Paul Licameli
2021-04-08 14:56:06 -04:00
committed by GitHub
parent cceaf0d162
commit 40d3a36296
2 changed files with 25 additions and 18 deletions

View File

@@ -155,7 +155,20 @@ int DBConnection::Open(const FilePath fileName)
mCheckpointStop = false; mCheckpointStop = false;
mCheckpointPending = false; mCheckpointPending = false;
mCheckpointActive = 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 // Install our checkpoint hook
sqlite3_wal_hook(mDB, CheckpointHook, this); sqlite3_wal_hook(mDB, CheckpointHook, this);
@@ -213,6 +226,7 @@ bool DBConnection::Close()
} }
// And wait for it to do so // And wait for it to do so
if (mCheckpointThread.joinable())
mCheckpointThread.join(); mCheckpointThread.join();
// We're done with the prepared statements // We're done with the prepared statements
@@ -373,22 +387,14 @@ sqlite3_stmt *DBConnection::Prepare(enum StatementID id, const char *sql)
return stmt; return stmt;
} }
void DBConnection::CheckpointThread() void DBConnection::CheckpointThread(sqlite3 *db)
{ {
// Open another connection to the DB to prevent blocking the main thread. int rc = SQLITE_OK;
//
// 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);
bool giveUp = false; bool giveUp = false;
const char *name = nullptr;
auto rc = sqlite3_open(name, &db); if (db)
if (rc == SQLITE_OK)
{ {
// Configure it to be safe name = sqlite3_db_filename(db, nullptr);
ModeConfig(db, "main", SafeConfig);
while (true) while (true)
{ {
{ {
@@ -484,8 +490,9 @@ void DBConnection::CheckpointThread()
} }
// All done (always close) // All done (always close)
if (db)
rc = sqlite3_close(db); rc = sqlite3_close(db);
if (rc != SQLITE_OK) if (rc != SQLITE_OK && name)
{ {
wxLogMessage("Checkpoint thread failed to close %s\n" wxLogMessage("Checkpoint thread failed to close %s\n"
"\tErrCode: %d\n" "\tErrCode: %d\n"

View File

@@ -98,7 +98,7 @@ public:
private: private:
bool ModeConfig(sqlite3 *db, const char *schema, const char *config); 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); static int CheckpointHook(void *data, sqlite3 *db, const char *schema, int pages);
private: private: