1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-23 15:50:05 +02:00

Access BlockFile::mFileName without copying, with proper multithreading cautions

This commit is contained in:
Paul Licameli 2016-04-17 11:42:33 -04:00
parent 94cf94718e
commit 3d102a3390
8 changed files with 65 additions and 32 deletions

View File

@ -120,9 +120,9 @@ BlockFile::~BlockFile()
/// but most BlockFiles have at least their summary data here. /// but most BlockFiles have at least their summary data here.
/// (some, i.e. SilentBlockFiles, do not correspond to a file on /// (some, i.e. SilentBlockFiles, do not correspond to a file on
/// disk and have empty file names) /// disk and have empty file names)
wxFileName BlockFile::GetFileName() const auto BlockFile::GetFileName() const -> GetFileNameResult
{ {
return mFileName; return { mFileName };
} }
///sets the file name the summary info will be saved in. threadsafe. ///sets the file name the summary info will be saved in. threadsafe.

View File

@ -24,6 +24,8 @@
#include "wxFileNameWrapper.h" #include "wxFileNameWrapper.h"
#include "ondemand/ODTaskThread.h"
class SummaryInfo { class SummaryInfo {
public: public:
@ -72,7 +74,24 @@ class PROFILE_DLL_API BlockFile /* not final, abstract */ {
/// Gets the filename of the disk file associated with this BlockFile /// Gets the filename of the disk file associated with this BlockFile
/// (can be empty -- some BlockFiles, like SilentBlockFile, correspond to /// (can be empty -- some BlockFiles, like SilentBlockFile, correspond to
/// no file on disk) /// no file on disk)
virtual wxFileName GetFileName() const; /// Avoids copying wxFileName by returning a reference, but for some subclasses
/// of BlockFile, you must exclude other threads from changing the name so long
/// as you have only a reference. Thus, this wrapper object that guarantees release
/// of any lock when it goes out of scope. Call mLocker.reset() to unlock it sooner.
struct GetFileNameResult {
const wxFileName &name;
ODLocker mLocker;
GetFileNameResult(const wxFileName &name_, ODLocker &&locker = ODLocker{})
: name{ name_ }, mLocker{ std::move(locker) } {}
GetFileNameResult(const GetFileNameResult&) PROHIBITED;
GetFileNameResult &operator= (const GetFileNameResult&) PROHIBITED;
GetFileNameResult(GetFileNameResult &&that)
: name{ that.name }, mLocker{ std::move(that.mLocker) } {}
};
virtual GetFileNameResult GetFileName() const;
virtual void SetFileName(wxFileNameWrapper &&name); virtual void SetFileName(wxFileNameWrapper &&name);
virtual sampleCount GetLength() const { return mLen; } virtual sampleCount GetLength() const { return mLen; }

View File

@ -941,7 +941,8 @@ bool DirManager::ContainsBlockFile(const BlockFile *b) const
{ {
if (!b) if (!b)
return false; return false;
BlockHash::const_iterator it = mBlockFileHash.find(b->GetFileName().GetName()); auto result = b->GetFileName();
BlockHash::const_iterator it = mBlockFileHash.find(result.name.GetName());
return it != mBlockFileHash.end() && it->second == b; return it != mBlockFileHash.end() && it->second == b;
} }
@ -957,7 +958,8 @@ bool DirManager::ContainsBlockFile(const wxString &filepath) const
// the BlockFile. // the BlockFile.
BlockFile *DirManager::CopyBlockFile(BlockFile *b) BlockFile *DirManager::CopyBlockFile(BlockFile *b)
{ {
const auto &fn = b->GetFileName(); auto result = b->GetFileName();
const auto &fn = result.name;
if (!b->IsLocked()) { if (!b->IsLocked()) {
b->Ref(); b->Ref();
@ -996,6 +998,9 @@ BlockFile *DirManager::CopyBlockFile(BlockFile *b)
return NULL; return NULL;
} }
// Done with fn
result.mLocker.reset();
b2 = b->Copy(std::move(newFile)); b2 = b->Copy(std::move(newFile));
if (b2 == NULL) if (b2 == NULL)
@ -1090,7 +1095,7 @@ bool DirManager::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
// return a reference to the existing object instead. // return a reference to the existing object instead.
// //
wxString name = target->GetFileName().GetName(); wxString name = target->GetFileName().name.GetName();
BlockFile *retrieved = mBlockFileHash[name]; BlockFile *retrieved = mBlockFileHash[name];
if (retrieved) { if (retrieved) {
// Lock it in order to DELETE it safely, i.e. without having // Lock it in order to DELETE it safely, i.e. without having
@ -1114,23 +1119,24 @@ bool DirManager::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
bool DirManager::MoveOrCopyToNewProjectDirectory(BlockFile *f, bool copy) bool DirManager::MoveOrCopyToNewProjectDirectory(BlockFile *f, bool copy)
{ {
const auto oldFileName = f->GetFileName(); auto result = f->GetFileName();
const auto &oldFileNameRef = result.name;
// Check that this BlockFile corresponds to a file on disk // Check that this BlockFile corresponds to a file on disk
//ANSWER-ME: Is this checking only for SilentBlockFiles, in which case //ANSWER-ME: Is this checking only for SilentBlockFiles, in which case
// (!oldFileName.IsOk()) is a more correct check? // (!oldFileName.IsOk()) is a more correct check?
if (oldFileName.GetName().IsEmpty()) { if (oldFileNameRef.GetName().IsEmpty()) {
return true; return true;
} }
wxFileNameWrapper newFileName; wxFileNameWrapper newFileName;
if (!this->AssignFile(newFileName, oldFileName.GetFullName(), false)) if (!this->AssignFile(newFileName, oldFileNameRef.GetFullName(), false))
return false; return false;
if (newFileName != oldFileName) { if (newFileName != oldFileNameRef) {
//check to see that summary exists before we copy. //check to see that summary exists before we copy.
bool summaryExisted = f->IsSummaryAvailable(); bool summaryExisted = f->IsSummaryAvailable();
auto oldPath = oldFileName.GetFullPath(); auto oldPath = oldFileNameRef.GetFullPath();
auto newPath = newFileName.GetFullPath(); auto newPath = newFileName.GetFullPath();
if (summaryExisted) { if (summaryExisted) {
auto success = copy auto success = copy
@ -1139,11 +1145,21 @@ bool DirManager::MoveOrCopyToNewProjectDirectory(BlockFile *f, bool copy)
if (!success) if (!success)
return false; return false;
} }
f->SetFileName(std::move(newFileName));
//there is a small chance that the summary has begun to be computed on a different thread with the
//original filename. we need to catch this case by waiting for it to finish and then copy.
if (!summaryExisted && (f->IsSummaryAvailable() || f->IsSummaryBeingComputed())) { if (!summaryExisted && (f->IsSummaryAvailable() || f->IsSummaryBeingComputed())) {
// We will need to remember the old file name, so copy it
wxFileName oldFileName{ oldFileNameRef };
// Now we can free any lock (and should, if as the comment below says, we need
// the other threads to progress)
result.mLocker.reset();
f->SetFileName(std::move(newFileName));
//there is a small chance that the summary has begun to be computed on a different thread with the
//original filename. we need to catch this case by waiting for it to finish and then copy.
//block to make sure OD files don't get written while we are changing file names. //block to make sure OD files don't get written while we are changing file names.
//(It is important that OD files set this lock while computing their summary files.) //(It is important that OD files set this lock while computing their summary files.)
while(f->IsSummaryBeingComputed() && !f->IsSummaryAvailable()) while(f->IsSummaryBeingComputed() && !f->IsSummaryAvailable())
@ -1160,6 +1176,11 @@ bool DirManager::MoveOrCopyToNewProjectDirectory(BlockFile *f, bool copy)
return false; return false;
} }
} }
else {
// Can free this now, and must, because of nonrecursive mutexes
result.mLocker.reset();
f->SetFileName(std::move(newFileName));
}
} }
return true; return true;
@ -1190,7 +1211,7 @@ int DirManager::GetRefCount(BlockFile * f)
void DirManager::Deref(BlockFile * f) void DirManager::Deref(BlockFile * f)
{ {
wxString theFileName = f->GetFileName().GetName(); const wxString theFileName = f->GetFileName().name.GetName();
//printf("Deref(%d): %s\n", //printf("Deref(%d): %s\n",
// f->mRefCount-1, // f->mRefCount-1,
@ -1338,7 +1359,8 @@ bool DirManager::EnsureSafeFilename(const wxFileName &fName)
{ {
ab->ChangeAliasedFileName(wxFileNameWrapper{ renamedFileName }); ab->ChangeAliasedFileName(wxFileNameWrapper{ renamedFileName });
ab->UnlockRead(); ab->UnlockRead();
wxPrintf(_("Changed block %s to new alias name\n"), b->GetFileName().GetFullName().c_str()); wxPrintf(_("Changed block %s to new alias name\n"),
b->GetFileName().name.GetFullName().c_str());
} }
else if (!b->IsDataAvailable() && db->GetEncodedAudioFilename() == fName) { else if (!b->IsDataAvailable() && db->GetEncodedAudioFilename() == fName) {

View File

@ -987,7 +987,7 @@ void Sequence::HandleXMLEndTag(const wxChar *tag)
for (unsigned b = 0, nn = mBlock.size(); b < nn; b++) { for (unsigned b = 0, nn = mBlock.size(); b < nn; b++) {
SeqBlock &block = mBlock[b]; SeqBlock &block = mBlock[b];
if (block.start != numSamples) { if (block.start != numSamples) {
wxString sFileAndExtension = block.f->GetFileName().GetFullName(); wxString sFileAndExtension = block.f->GetFileName().name.GetFullName();
if (sFileAndExtension.IsEmpty()) if (sFileAndExtension.IsEmpty())
sFileAndExtension = wxT("(replaced with silence)"); sFileAndExtension = wxT("(replaced with silence)");
else else
@ -1829,7 +1829,7 @@ void Sequence::DebugPrintf(wxString *dest) const
seqBlock.f ? mDirManager->GetRefCount(seqBlock.f) : 0); seqBlock.f ? mDirManager->GetRefCount(seqBlock.f) : 0);
if (seqBlock.f) if (seqBlock.f)
*dest += seqBlock.f->GetFileName().GetFullName(); *dest += seqBlock.f->GetFileName().name.GetFullName();
else else
*dest += wxT("<missing block file>"); *dest += wxT("<missing block file>");

View File

@ -369,13 +369,9 @@ void ODDecodeBlockFile::SetFileName(wxFileNameWrapper &&name)
} }
///sets the file name the summary info will be saved in. threadsafe. ///sets the file name the summary info will be saved in. threadsafe.
wxFileName ODDecodeBlockFile::GetFileName() const auto ODDecodeBlockFile::GetFileName() const -> GetFileNameResult
{ {
wxFileName name; return { mFileName, ODLocker{ &mFileNameMutex } };
mFileNameMutex.Lock();
name = mFileName;
mFileNameMutex.Unlock();
return name;
} }
/// A thread-safe version of CalcSummary. BlockFile::CalcSummary /// A thread-safe version of CalcSummary. BlockFile::CalcSummary

View File

@ -133,7 +133,7 @@ class ODDecodeBlockFile final : public SimpleBlockFile
///sets the file name the summary info will be saved in. threadsafe. ///sets the file name the summary info will be saved in. threadsafe.
void SetFileName(wxFileNameWrapper &&name) override; void SetFileName(wxFileNameWrapper &&name) override;
wxFileName GetFileName() const override; GetFileNameResult GetFileName() const override;
/// Prevents a read on other threads of the encoded audio file. /// Prevents a read on other threads of the encoded audio file.
void LockRead() const override; void LockRead() const override;

View File

@ -381,13 +381,9 @@ void ODPCMAliasBlockFile::SetFileName(wxFileNameWrapper &&name)
} }
///sets the file name the summary info will be saved in. threadsafe. ///sets the file name the summary info will be saved in. threadsafe.
wxFileName ODPCMAliasBlockFile::GetFileName() const auto ODPCMAliasBlockFile::GetFileName() const -> GetFileNameResult
{ {
wxFileName name; return { mFileName, ODLocker{ &mFileNameMutex } };
mFileNameMutex.Lock();
name = mFileName;
mFileNameMutex.Unlock();
return name;
} }
/// Write the summary to disk, using the derived ReadData() to get the data /// Write the summary to disk, using the derived ReadData() to get the data

View File

@ -125,7 +125,7 @@ class ODPCMAliasBlockFile final : public PCMAliasBlockFile
///sets the file name the summary info will be saved in. threadsafe. ///sets the file name the summary info will be saved in. threadsafe.
void SetFileName(wxFileNameWrapper &&name) override; void SetFileName(wxFileNameWrapper &&name) override;
wxFileName GetFileName() const override; GetFileNameResult GetFileName() const override;
//when the file closes, it locks the blockfiles, but only conditionally. //when the file closes, it locks the blockfiles, but only conditionally.
// It calls this so we can check if it has been saved before. // It calls this so we can check if it has been saved before.