1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-09-18 17:10:55 +02:00

Preliminaries for fixing Bug2550 residuals

This commit is contained in:
Paul Licameli 2020-12-06 23:23:09 -05:00
commit 98d7218e31
6 changed files with 89 additions and 29 deletions

View File

@ -44,6 +44,7 @@ Paul Licameli split from AudacityProject.cpp
wxDEFINE_EVENT(EVT_PROJECT_TITLE_CHANGE, wxCommandEvent);
wxDEFINE_EVENT( EVT_CHECKPOINT_FAILURE, wxCommandEvent);
wxDEFINE_EVENT( EVT_RECONNECTION_FAILURE, wxCommandEvent);
static const int ProjectFileID = ('A' << 24 | 'U' << 16 | 'D' << 8 | 'Y');
static const int ProjectFileVersion = 1;
@ -263,6 +264,12 @@ ProjectFileIO::~ProjectFileIO()
{
}
bool ProjectFileIO::HasConnection() const
{
auto &connectionPtr = ConnectionPtr::Get( mProject );
return connectionPtr.mpConnection != nullptr;
}
DBConnection &ProjectFileIO::GetConnection()
{
auto &curConn = CurrConn();
@ -331,6 +338,7 @@ bool ProjectFileIO::OpenConnection(FilePath fileName /* = {} */)
if (!CheckVersion())
{
CloseConnection();
curConn.reset();
return false;
}
@ -344,7 +352,8 @@ bool ProjectFileIO::OpenConnection(FilePath fileName /* = {} */)
bool ProjectFileIO::CloseConnection()
{
auto &curConn = CurrConn();
wxASSERT(curConn);
if (!curConn)
return false;
if (!curConn->Close())
{
@ -689,6 +698,10 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
bool prune /* = false */,
const std::vector<const TrackList *> &tracks /* = {} */)
{
auto pConn = CurrConn().get();
if (!pConn)
return false;
// Get access to the active tracklist
auto pProject = &mProject;
@ -763,7 +776,7 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
//
// NOTE: Between the above attach and setting the mode here, a normal DELETE
// mode journal will be used and will briefly appear in the filesystem.
CurrConn()->FastMode("outbound");
pConn->FastMode("outbound");
// Install our schema into the new database
if (!InstallSchema(db, "outbound"))
@ -2039,20 +2052,38 @@ bool ProjectFileIO::SaveProject(
FilePath savedName = mFileName;
if (CloseConnection())
{
if (MoveProject(savedName, fileName))
bool reopened = false;
bool moved = false;
if (true == (moved = MoveProject(savedName, fileName)))
{
if (!OpenConnection(fileName))
{
if (OpenConnection(fileName))
reopened = true;
else {
MoveProject(fileName, savedName);
OpenConnection(savedName);
reopened = OpenConnection(savedName);
}
}
else {
// Rename can fail -- if it's to a different device, requiring
// real copy of contents, which might exhaust space
OpenConnection(savedName);
return false;
reopened = OpenConnection(savedName);
}
if (!reopened)
wxTheApp->CallAfter([this]{
ShowErrorDialog(nullptr,
XO("Warning"),
XO(
"The project's database failed to reopen, "
"possibly because of limited space on the storage device."),
"Error:_Disk_full_or_not_writable"
);
wxCommandEvent evt{ EVT_RECONNECTION_FAILURE };
mProject.ProcessEvent(evt);
});
if (!moved)
return false;
}
}
@ -2179,11 +2210,9 @@ bool ProjectFileIO::OpenProject()
bool ProjectFileIO::CloseProject()
{
auto &currConn = CurrConn();
wxASSERT(currConn);
// Protect...
if (!currConn)
{
wxLogDebug("Closing project with no database connection");
return true;
}
@ -2270,14 +2299,16 @@ void ProjectFileIO::SetError(
const TranslatableString &msg, const TranslatableString &libraryError )
{
auto &currConn = CurrConn();
currConn->SetError(msg, libraryError);
if (currConn)
currConn->SetError(msg, libraryError);
}
void ProjectFileIO::SetDBError(
const TranslatableString &msg, const TranslatableString &libraryError)
{
auto &currConn = CurrConn();
currConn->SetDBError(msg, libraryError);
if (currConn)
currConn->SetDBError(msg, libraryError);
}
void ProjectFileIO::SetBypass()
@ -2316,7 +2347,10 @@ void ProjectFileIO::SetBypass()
int64_t ProjectFileIO::GetBlockUsage(SampleBlockID blockid)
{
return GetDiskUsage(CurrConn().get(), blockid);
auto pConn = CurrConn().get();
if (!pConn)
return 0;
return GetDiskUsage(*pConn, blockid);
}
int64_t ProjectFileIO::GetCurrentUsage(
@ -2337,7 +2371,10 @@ int64_t ProjectFileIO::GetCurrentUsage(
int64_t ProjectFileIO::GetTotalUsage()
{
return GetDiskUsage(CurrConn().get(), 0);
auto pConn = CurrConn().get();
if (!pConn)
return 0;
return GetDiskUsage(*pConn, 0);
}
//
@ -2346,7 +2383,7 @@ int64_t ProjectFileIO::GetTotalUsage()
// pages available from the "sqlite_dbpage" virtual table to traverse the SQLite
// table b-tree described here: https://www.sqlite.org/fileformat.html
//
int64_t ProjectFileIO::GetDiskUsage(DBConnection *conn, SampleBlockID blockid /* = 0 */)
int64_t ProjectFileIO::GetDiskUsage(DBConnection &conn, SampleBlockID blockid /* = 0 */)
{
// Information we need to track our travels through the b-tree
typedef struct
@ -2365,7 +2402,7 @@ int64_t ProjectFileIO::GetDiskUsage(DBConnection *conn, SampleBlockID blockid /*
// Get the rootpage for the sampleblocks table.
sqlite3_stmt *stmt =
conn->Prepare(DBConnection::GetRootPage,
conn.Prepare(DBConnection::GetRootPage,
"SELECT rootpage FROM sqlite_master WHERE tbl_name = 'sampleblocks';");
if (stmt == nullptr || sqlite3_step(stmt) != SQLITE_ROW)
{
@ -2380,7 +2417,7 @@ int64_t ProjectFileIO::GetDiskUsage(DBConnection *conn, SampleBlockID blockid /*
sqlite3_reset(stmt);
// Prepare/retrieve statement to read raw database page
stmt = conn->Prepare(DBConnection::GetDBPage,
stmt = conn.Prepare(DBConnection::GetDBPage,
"SELECT data FROM sqlite_dbpage WHERE pgno = ?1;");
if (stmt == nullptr)
{

View File

@ -45,6 +45,11 @@ using BlockIDs = std::unordered_set<SampleBlockID>;
wxDECLARE_EXPORTED_EVENT( AUDACITY_DLL_API,
EVT_CHECKPOINT_FAILURE, wxCommandEvent );
// An event processed by the project in the main thread after failure to
// reconnect to the database, after temporary close and attempted file movement
wxDECLARE_EXPORTED_EVENT( AUDACITY_DLL_API,
EVT_RECONNECTION_FAILURE, wxCommandEvent );
///\brief Object associated with a project that manages reading and writing
/// of Audacity project file formats, and autosave
class ProjectFileIO final
@ -108,7 +113,7 @@ public:
// Return the bytes used for the given block using the connection to a
// specific database. This is the workhorse for the above 3 methods.
static int64_t GetDiskUsage(DBConnection *conn, SampleBlockID blockid);
static int64_t GetDiskUsage(DBConnection &conn, SampleBlockID blockid);
const TranslatableString &GetLastError();
const TranslatableString &GetLibraryError();
@ -179,6 +184,10 @@ public:
// 0 for success or non-zero to stop the query
using ExecCB = std::function<int(int cols, char **vals, char **names)>;
//! Return true if a connetion is now open
bool HasConnection() const;
//! Return a reference to a connection, creating it as needed on demand; throw on failure
DBConnection &GetConnection();
private:

View File

@ -302,15 +302,17 @@ bool ProjectFileManager::DoSave(const FilePath & fileName, const bool fromSaveAs
bool success = projectFileIO.SaveProject(fileName, mLastSavedTracks.get());
if (!success)
{
ShowErrorDialog(
&window,
XO("Error Saving Project"),
XO("Could not save project. Perhaps %s \n"
"is not writable or the disk is full.\n"
"For tips on freeing up space, click the help button.")
.Format(fileName),
"Error:_Disk_full_or_not_writable"
);
// Show this error only if we didn't fail reconnection in SaveProject
if (projectFileIO.HasConnection())
ShowErrorDialog(
&window,
XO("Error Saving Project"),
XO("Could not save project. Perhaps %s \n"
"is not writable or the disk is full.\n"
"For tips on freeing up space, click the help button.")
.Format(fileName),
"Error:_Disk_full_or_not_writable"
);
return false;
}

View File

@ -82,6 +82,8 @@ ProjectManager::ProjectManager( AudacityProject &project )
window.Bind( wxEVT_CLOSE_WINDOW, &ProjectManager::OnCloseWindow, this );
mProject.Bind(EVT_PROJECT_STATUS_UPDATE,
&ProjectManager::OnStatusChange, this);
project.Bind( EVT_RECONNECTION_FAILURE,
&ProjectManager::OnReconnectionFailure, this );
}
ProjectManager::~ProjectManager() = default;
@ -589,6 +591,14 @@ AudacityProject *ProjectManager::New()
return p;
}
void ProjectManager::OnReconnectionFailure(wxCommandEvent & event)
{
event.Skip();
wxTheApp->CallAfter([this]{
ProjectWindow::Get(mProject).Close(true);
});
}
void ProjectManager::OnCloseWindow(wxCloseEvent & event)
{
auto &project = mProject;

View File

@ -65,6 +65,7 @@ public:
void SetSkipSavePrompt(bool bSkip) { sbSkipPromptingForSave = bSkip; };
private:
void OnReconnectionFailure(wxCommandEvent & event);
void OnCloseWindow(wxCloseEvent & event);
void OnTimer(wxTimerEvent & event);
void OnOpenAudioFile(wxCommandEvent & event);

View File

@ -88,6 +88,7 @@ private:
private:
//! This must never be called for silent blocks
/*! @post return value is not null */
DBConnection *Conn() const;
sqlite3 *DB() const
{
@ -527,7 +528,7 @@ size_t SqliteSampleBlock::GetSpaceUsage() const
if (IsSilent())
return 0;
else
return ProjectFileIO::GetDiskUsage(Conn(), mBlockID);
return ProjectFileIO::GetDiskUsage(*Conn(), mBlockID);
}
size_t SqliteSampleBlock::GetBlob(void *dest,