mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-30 15:49:41 +02:00
AUP3: Put checkpointing in a separate thread
This is highly experimental. It's defers most checkpointing to a separate thread to see if we get better throughput and less choppiness when applying effects.
This commit is contained in:
parent
e669b365f1
commit
d2b4a0e488
@ -23,9 +23,7 @@ list( APPEND DEFINES
|
|||||||
# Connection based settings. Persistent settings are done in the
|
# Connection based settings. Persistent settings are done in the
|
||||||
# schema.
|
# schema.
|
||||||
#
|
#
|
||||||
SQLITE_DEFAULT_LOCKING_MODE=1
|
# SQLITE_DEFAULT_LOCKING_MODE=0
|
||||||
SQLITE_DEFAULT_WAL_AUTOCHECKPOINT=100
|
|
||||||
SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=50000000
|
|
||||||
|
|
||||||
# Can't be set after a WAL mode database is initialized, so change
|
# Can't be set after a WAL mode database is initialized, so change
|
||||||
# the default here to ensure all project files get the same page
|
# the default here to ensure all project files get the same page
|
||||||
|
@ -17,8 +17,6 @@ Paul Licameli split from AudacityProject.cpp
|
|||||||
#include <wx/sstream.h>
|
#include <wx/sstream.h>
|
||||||
#include <wx/xml/xml.h>
|
#include <wx/xml/xml.h>
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "FileNames.h"
|
#include "FileNames.h"
|
||||||
#include "Project.h"
|
#include "Project.h"
|
||||||
#include "ProjectFileIORegistry.h"
|
#include "ProjectFileIORegistry.h"
|
||||||
@ -129,10 +127,10 @@ static const char *ProjectFileSchema =
|
|||||||
|
|
||||||
// Configuration to provide "safe" connections
|
// Configuration to provide "safe" connections
|
||||||
static const char *SafeConfig =
|
static const char *SafeConfig =
|
||||||
"PRAGMA <schema>.locking_mode = EXCLUSIVE;"
|
"PRAGMA <schema>.locking_mode = SHARED;"
|
||||||
"PRAGMA <schema>.synchronous = NORMAL;"
|
"PRAGMA <schema>.synchronous = NORMAL;"
|
||||||
"PRAGMA <schema>.journal_mode = WAL;"
|
"PRAGMA <schema>.journal_mode = WAL;"
|
||||||
"PRAGMA <schema>.wal_autocheckpoint = 10000;";
|
"PRAGMA <schema>.wal_autocheckpoint = 0;";
|
||||||
|
|
||||||
// Configuration to provide "Fast" connections
|
// Configuration to provide "Fast" connections
|
||||||
static const char *FastConfig =
|
static const char *FastConfig =
|
||||||
@ -144,16 +142,7 @@ static const char *FastConfig =
|
|||||||
static const char *FastWalConfig =
|
static const char *FastWalConfig =
|
||||||
"PRAGMA <schema>.locking_mode = EXCLUSIVE;"
|
"PRAGMA <schema>.locking_mode = EXCLUSIVE;"
|
||||||
"PRAGMA <schema>.synchronous = OFF;"
|
"PRAGMA <schema>.synchronous = OFF;"
|
||||||
"PRAGMA <schema>.journal_mode = WAL;"
|
"PRAGMA <schema>.journal_mode = WAL;";
|
||||||
"PRAGMA <schema>.wal_autocheckpoint = 10000;";
|
|
||||||
|
|
||||||
// Configuration to provide "Fast" connections
|
|
||||||
static const char *FastMemoryConfig =
|
|
||||||
"PRAGMA <schema>.locking_mode = EXCLUSIVE;"
|
|
||||||
"PRAGMA <schema>.synchronous = NORMAL;"
|
|
||||||
"PRAGMA <schema>.journal_mode = MEMORY;"
|
|
||||||
"PRAGMA <schema>.wal_autocheckpoint = 1000;";
|
|
||||||
|
|
||||||
|
|
||||||
// This singleton handles initialization/shutdown of the SQLite library.
|
// This singleton handles initialization/shutdown of the SQLite library.
|
||||||
// It is needed because our local SQLite is built with SQLITE_OMIT_AUTOINIT
|
// It is needed because our local SQLite is built with SQLITE_OMIT_AUTOINIT
|
||||||
@ -282,6 +271,10 @@ void ProjectFileIO::Init( AudacityProject &project )
|
|||||||
// This step can't happen in the ctor of ProjectFileIO because ctor of
|
// This step can't happen in the ctor of ProjectFileIO because ctor of
|
||||||
// AudacityProject wasn't complete
|
// AudacityProject wasn't complete
|
||||||
mpProject = project.shared_from_this();
|
mpProject = project.shared_from_this();
|
||||||
|
|
||||||
|
// Kick off the checkpoint thread
|
||||||
|
mCheckpointStop = false;
|
||||||
|
mCheckpointThread = std::thread([this]{ CheckpointThread(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectFileIO::~ProjectFileIO()
|
ProjectFileIO::~ProjectFileIO()
|
||||||
@ -308,6 +301,96 @@ ProjectFileIO::~ProjectFileIO()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tell the checkpoint thread to shutdown
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mCheckpointMutex);
|
||||||
|
mCheckpointStop = true;
|
||||||
|
mCheckpointCondition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// And wait for it to do so
|
||||||
|
mCheckpointThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define USE_DIFFERENT_DB_HANDLE 1
|
||||||
|
|
||||||
|
void ProjectFileIO::CheckpointThread()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
sqlite3 *db;
|
||||||
|
{
|
||||||
|
// Wait for something to show up in the queue or for the stop signal
|
||||||
|
std::unique_lock<std::mutex> lock(mCheckpointMutex);
|
||||||
|
mCheckpointCondition.wait(lock,
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
return mCheckpointStop || !mCheckpointWork.empty();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Requested to stop, so bail
|
||||||
|
if (mCheckpointStop)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop the head of the queue
|
||||||
|
db = mCheckpointWork.front();
|
||||||
|
mCheckpointWork.pop_front();
|
||||||
|
|
||||||
|
#if defined(USE_DIFFERENT_DB_HANDLE)
|
||||||
|
// Lock out other while the checkpoint is running
|
||||||
|
mCheckpointActive.lock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Get it's filename
|
||||||
|
const char *filename = sqlite3_db_filename(db, nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(USE_DIFFERENT_DB_HANDLE)
|
||||||
|
// Open it. We don't use the queued database pointer because that
|
||||||
|
// would block the main thread if it tried to use it at the same time.
|
||||||
|
db = nullptr;
|
||||||
|
if (sqlite3_open(filename, &db) == SQLITE_OK)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if defined(USE_DIFFERENT_DB_HANDLE)
|
||||||
|
// Configure it to be safe
|
||||||
|
Config(db, SafeConfig);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// And do the actual checkpoint. This may not checkpoint ALL frames
|
||||||
|
// in the WAL. They'll be gotten the next time round.
|
||||||
|
sqlite3_wal_checkpoint_v2(db, nullptr, SQLITE_CHECKPOINT_PASSIVE, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(USE_DIFFERENT_DB_HANDLE)
|
||||||
|
// All done...okay to call with null pointer if open failed.
|
||||||
|
sqlite3_close(db);
|
||||||
|
|
||||||
|
// Checkpoint is complete
|
||||||
|
mCheckpointActive.unlock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ProjectFileIO::CheckpointHook(void *data, sqlite3 *db, const char *schema, int pages)
|
||||||
|
{
|
||||||
|
// Get access to our object
|
||||||
|
ProjectFileIO *that = static_cast<ProjectFileIO *>(data);
|
||||||
|
|
||||||
|
// Qeuue the database pointer for our checkpoint thread to process
|
||||||
|
std::lock_guard<std::mutex> guard(that->mCheckpointMutex);
|
||||||
|
that->mCheckpointWork.push_back(db);
|
||||||
|
that->mCheckpointCondition.notify_one();
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3 *ProjectFileIO::DB()
|
sqlite3 *ProjectFileIO::DB()
|
||||||
@ -453,6 +536,9 @@ sqlite3 *ProjectFileIO::OpenDB(FilePath fileName)
|
|||||||
|
|
||||||
SetFileName(fileName);
|
SetFileName(fileName);
|
||||||
|
|
||||||
|
// Install our checkpoint hook
|
||||||
|
sqlite3_wal_hook(mDB, CheckpointHook, this);
|
||||||
|
|
||||||
return mDB;
|
return mDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,6 +548,27 @@ bool ProjectFileIO::CloseDB()
|
|||||||
|
|
||||||
if (mDB)
|
if (mDB)
|
||||||
{
|
{
|
||||||
|
// Uninstall our checkpoint hook
|
||||||
|
sqlite3_wal_hook(mDB, nullptr, nullptr);
|
||||||
|
|
||||||
|
// Remove this connection from the checkpoint work queue, if any
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mCheckpointMutex);
|
||||||
|
|
||||||
|
mCheckpointWork.erase(std::remove(mCheckpointWork.begin(),
|
||||||
|
mCheckpointWork.end(),
|
||||||
|
mDB),
|
||||||
|
mCheckpointWork.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(USE_DIFFERENT_DB_HANDLE)
|
||||||
|
// Wait for ANY active checkpoint to complete as it may be for
|
||||||
|
// this database
|
||||||
|
mCheckpointActive.lock();
|
||||||
|
mCheckpointActive.unlock();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Close the DB
|
||||||
rc = sqlite3_close(mDB);
|
rc = sqlite3_close(mDB);
|
||||||
if (rc != SQLITE_OK)
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
|
@ -11,8 +11,14 @@ Paul Licameli split from AudacityProject.h
|
|||||||
#ifndef __AUDACITY_PROJECT_FILE_IO__
|
#ifndef __AUDACITY_PROJECT_FILE_IO__
|
||||||
#define __AUDACITY_PROJECT_FILE_IO__
|
#define __AUDACITY_PROJECT_FILE_IO__
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "ClientData.h" // to inherit
|
#include "ClientData.h" // to inherit
|
||||||
#include "Prefs.h" // to inherit
|
#include "Prefs.h" // to inherit
|
||||||
#include "xml/XMLTagHandler.h" // to inherit
|
#include "xml/XMLTagHandler.h" // to inherit
|
||||||
@ -110,6 +116,10 @@ public:
|
|||||||
// The last vacuum check found unused blocks in the project file
|
// The last vacuum check found unused blocks in the project file
|
||||||
bool HadUnused();
|
bool HadUnused();
|
||||||
|
|
||||||
|
bool TransactionStart(const wxString &name);
|
||||||
|
bool TransactionCommit(const wxString &name);
|
||||||
|
bool TransactionRollback(const wxString &name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void WriteXMLHeader(XMLWriter &xmlFile) const;
|
void WriteXMLHeader(XMLWriter &xmlFile) const;
|
||||||
void WriteXML(XMLWriter &xmlFile, bool recording = false, const std::shared_ptr<TrackList> &tracks = nullptr) /* not override */;
|
void WriteXML(XMLWriter &xmlFile, bool recording = false, const std::shared_ptr<TrackList> &tracks = nullptr) /* not override */;
|
||||||
@ -155,10 +165,6 @@ private:
|
|||||||
bool CloseDB();
|
bool CloseDB();
|
||||||
bool DeleteDB();
|
bool DeleteDB();
|
||||||
|
|
||||||
bool TransactionStart(const wxString &name);
|
|
||||||
bool TransactionCommit(const wxString &name);
|
|
||||||
bool TransactionRollback(const wxString &name);
|
|
||||||
|
|
||||||
bool Query(const char *sql, ExecResult &result);
|
bool Query(const char *sql, ExecResult &result);
|
||||||
|
|
||||||
bool GetValue(const char *sql, wxString &value);
|
bool GetValue(const char *sql, wxString &value);
|
||||||
@ -189,6 +195,9 @@ private:
|
|||||||
|
|
||||||
bool ShouldVacuum(const std::shared_ptr<TrackList> &tracks);
|
bool ShouldVacuum(const std::shared_ptr<TrackList> &tracks);
|
||||||
|
|
||||||
|
void CheckpointThread();
|
||||||
|
static int CheckpointHook(void *that, sqlite3 *db, const char *schema, int pages);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// non-static data members
|
// non-static data members
|
||||||
std::weak_ptr<AudacityProject> mpProject;
|
std::weak_ptr<AudacityProject> mpProject;
|
||||||
@ -221,8 +230,12 @@ private:
|
|||||||
TranslatableString mLastError;
|
TranslatableString mLastError;
|
||||||
TranslatableString mLibraryError;
|
TranslatableString mLibraryError;
|
||||||
|
|
||||||
// Track the highest blockid while loading a project
|
std::deque<sqlite3 *> mCheckpointWork;
|
||||||
int64_t mHighestBlockID;
|
std::condition_variable mCheckpointCondition;
|
||||||
|
std::mutex mCheckpointMutex;
|
||||||
|
std::thread mCheckpointThread;
|
||||||
|
std::atomic_bool mCheckpointStop;
|
||||||
|
std::mutex mCheckpointActive;
|
||||||
|
|
||||||
friend SqliteSampleBlock;
|
friend SqliteSampleBlock;
|
||||||
friend AutoCommitTransaction;
|
friend AutoCommitTransaction;
|
||||||
|
@ -1318,19 +1318,12 @@ bool Effect::DoEffect(double projectRate,
|
|||||||
auto vr = valueRestorer( mProgress, &progress );
|
auto vr = valueRestorer( mProgress, &progress );
|
||||||
|
|
||||||
{
|
{
|
||||||
// This is for performance purposes only.
|
// This is for performance purposes only, no additional recovery implied
|
||||||
#if 0
|
|
||||||
auto &pProject = *const_cast<AudacityProject*>(FindProject()); // how to remove this const_cast?
|
auto &pProject = *const_cast<AudacityProject*>(FindProject()); // how to remove this const_cast?
|
||||||
auto &pIO = ProjectFileIO::Get(pProject);
|
auto &pIO = ProjectFileIO::Get(pProject);
|
||||||
AutoCommitTransaction trans(pIO, "Effect");
|
AutoCommitTransaction trans(pIO, "Effect");
|
||||||
#endif
|
|
||||||
returnVal = Process();
|
returnVal = Process();
|
||||||
#if 0
|
|
||||||
if (!returnVal)
|
|
||||||
{
|
|
||||||
trans.Rollback();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user