Rewrite comments about exception safety guarantees with Doxygen...

... Defining a new macro, which generates a special paragraph with links to a
new page describing the principles.
This commit is contained in:
Paul Licameli 2020-08-22 15:33:03 -04:00
parent 0e10a27172
commit 372393f49e
12 changed files with 253 additions and 116 deletions

View File

@ -2465,3 +2465,13 @@ GENERATE_LEGEND = YES
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
# More aliases
# @excsafety
# Example usage:
# "@excsafety{Strong} -- Leaves @c *this unchanged in case of exceptions"
# Argument may be Weak, Partial, Strong, No-fail, Mixed
# Generates a special paragraph with a link to a page section explaining the
# exception safety level
ALIASES += excsafety{1}="@par Exception safety guarantee:^^@ref \1-guarantee \"\1\""

View File

@ -0,0 +1,120 @@
/*!
@page exception-safety Exception safety guarantees
@brief Principles of robust error recovery
@tableofcontents
@section Weak-guarantee Weak guarantee
An operation providing a weak guarantee ensures that in case of errors
or exceptions, related objects remain in a consistent, but
unspecified, state. What constitutes "consistency" varies but at
minimum, each object can be safety destroyed or reassigned.
A collection of cooperating objects, each providing a
@ref Strong-guarantee "strong" guarantee for all of its methods, might only
implement a weak guarantee for the higher level of organization.
@section Partial-guarantee Partial guarantee
Intermediate between @ref Strong-guarantee "strong" and
@ref Weak-guarantee "weak", an operation may guarantee in case of errors
and exceptions some less than total degree of preservation of prior
data against loss, described in documentation comments.
@section Strong-guarantee Strong guarantee
An operation providing a strong guarantee ensures that in case of
errors or exceptions, all related objects remain in the same state as
they were in at the beginning.
"Same" might refer only to logical
contents, not necessarily bitwise, physical identity.
This is not the strongest guarantee -- that would be
@ref No-fail-guarantee "no-fail".
@subsection Strong-guarantee-compound Strong guarantee for compound operations
To reassign a complex object with a strong exception safety guarantee,
the copy-and-swap C++ idiom is often used:
- A temporary copy is built, risking exceptions at multiple places.
- If a later stage of this construction fails,
@ref No-throw-guarantee "no-throw" destructors for the
earlier stages are invoked to reclaim resources allocated to the
partial object.
- Only after all possibility of exceptions, a @ref No-throw-guarantee
"no-throw" swap changes the contents of @c *this.
- The @ref No-throw-guarantee "no-throw" destructor of the temporary destroys
the old contents.
If the swap is never reached, then the contents of @c *this are left
unchanged.
What is described next can be seen as a generalization of that idiom.
To provide a strong guarantee of a compound operation, another common
pattern is this:
-# for each step, implement a separate strong guarantee in case of
failure,
-# for each step (except perhaps the last), implement a
@ref No-fail-guarantee "no-fail" rollback operation that can undo it even
after success,
-# initialize a variable, typically called @c success or @c committed,
to @c false,
-# write one or more @ref finally blocks that invoke the rollbacks if the
variable remains false,
-# invoke the sequence of fallible, strongly guaranteed steps,
-# only now, at the point of assured success, set the variable to
@c true, so that the @ref finally blocks do nothing, and invoke other
finalization steps, which must be
@ref No-fail-guarantee "no-fail".
Steps 4 and 5 might interleave, but it is very important to order all
of step 5 before all of step 6.
@section No-throw-guarantee No-throw guarantee
An operation providing a no-throw guarantee will never allow an
exception to escape, unless for certain conditions such as memory
exhaustion that the program treats as absolutely unrecoverable.
This guarantee refers only to the programming language technicality of
exception propagation, and is not the same as the stronger
@ref No-fail-guarantee "No-fail" guarantee.
This is almost always a requirement for a C++ destructor. This
includes a lambda as argument to the function template @ref finally,
which generates an ad hoc destructor.
If a class has a swap operation, that too should be no-throw.
@section No-fail-guarantee No-fail guarantee
An operation providing a no-fail guarantee will never indicate failure
by any means, whether that be an error return code or an escaping
exception. It will succeed unless in case of absolutely unrecoverable
contingencies such as memory exhaustion or power failure.
No-fail guarantees are often necessary in functions that perform a
final phase commit of a collection of related changes or that perform
rollbacks of unsuccessful changes -- whether for a database in the
strict sense or analogously.
@section Mixed-guarantee Mixed guarantees
Some operations may provide a mix of guarantees: such as, that some
condition will hold without fail whether the operation finishes
normally or exceptionally; yet, only weak or strong guarantees are
given with respect to other conditions. Documentation comments should
describe details.
*/

View File

@ -2365,8 +2365,8 @@ void AudioIO::StopStream()
GuardedCall( [&] {
WaveTrack* track = mCaptureTracks[i].get();
// use NOFAIL-GUARANTEE that track is flushed,
// PARTIAL-GUARANTEE that some initial length of the recording
// use No-fail-guarantee that track is flushed,
// Partial-guarantee that some initial length of the recording
// is saved.
// See comments in FillBuffers().
track->Flush();

View File

@ -371,8 +371,8 @@ void Envelope::Insert(double when, double value)
mEnv.push_back( EnvPoint{ when, value });
}
/*! @excsafety{No-fail} */
void Envelope::CollapseRegion( double t0, double t1, double sampleDur )
// NOFAIL-GUARANTEE
{
if ( t1 <= t0 )
return;
@ -459,8 +459,8 @@ void Envelope::CollapseRegion( double t0, double t1, double sampleDur )
// envelope point applies to the first sample, but the t=tracklen
// envelope point applies one-past the last actual sample.
// t0 should be in the domain of this; if not, it is trimmed.
/*! @excsafety{No-fail} */
void Envelope::PasteEnvelope( double t0, const Envelope *e, double sampleDur )
// NOFAIL-GUARANTEE
{
const bool wasEmpty = (this->mEnv.size() == 0);
auto otherSize = e->mEnv.size();
@ -542,9 +542,9 @@ void Envelope::PasteEnvelope( double t0, const Envelope *e, double sampleDur )
ConsistencyCheck();
}
/*! @excsafety{No-fail} */
void Envelope::RemoveUnneededPoints
( size_t startAt, bool rightward, bool testNeighbors )
// NOFAIL-GUARANTEE
{
// startAt is the index of a recently inserted point which might make no
// difference in envelope evaluation, or else might cause nearby points to
@ -608,9 +608,9 @@ void Envelope::RemoveUnneededPoints
}
}
/*! @excsafety{No-fail} */
std::pair< int, int > Envelope::ExpandRegion
( double t0, double tlen, double *pLeftVal, double *pRightVal )
// NOFAIL-GUARANTEE
{
// t0 is relative time
@ -659,8 +659,8 @@ std::pair< int, int > Envelope::ExpandRegion
return { 1 + range.first, index };
}
/*! @excsafety{No-fail} */
void Envelope::InsertSpace( double t0, double tlen )
// NOFAIL-GUARANTEE
{
auto range = ExpandRegion( t0 - mOffset, tlen, nullptr, nullptr );
@ -780,14 +780,14 @@ std::pair<int, int> Envelope::EqualRange( double when, double sampleDur ) const
// Control
/*! @excsafety{No-fail} */
void Envelope::SetOffset(double newOffset)
// NOFAIL-GUARANTEE
{
mOffset = newOffset;
}
/*! @excsafety{No-fail} */
void Envelope::SetTrackLen( double trackLen, double sampleDur )
// NOFAIL-GUARANTEE
{
// Preserve the left-side limit at trackLen.
auto range = EqualRange( trackLen, sampleDur );
@ -807,8 +807,8 @@ void Envelope::SetTrackLen( double trackLen, double sampleDur )
AddPointAtEnd( mTrackLen, value );
}
/*! @excsafety{No-fail} */
void Envelope::RescaleTimes( double newLength )
// NOFAIL-GUARANTEE
{
if ( mTrackLen == 0 ) {
for ( auto &point : mEnv )

View File

@ -12,4 +12,8 @@
class wxFileName;
//! Update Audacity 1.0 file in-place to XML format
/*! @return true if successful, else no effect on the file
@excsafety{Strong}
*/
bool ConvertLegacyProjectFile(const wxFileName &filename);

View File

@ -94,12 +94,12 @@ auto ProjectAudioManager::StatusWidthFunction(
return {};
}
/*! @excsafety{Strong} -- For state of mCutPreviewTracks */
int ProjectAudioManager::PlayPlayRegion(const SelectedRegion &selectedRegion,
const AudioIOStartStreamOptions &options,
PlayMode mode,
bool backwards, /* = false */
bool playWhiteSpace /* = false */)
// STRONG-GUARANTEE (for state of mCutPreviewTracks)
{
auto &projectAudioManager = *this;
bool canStop = projectAudioManager.CanStopAudioStream();
@ -458,8 +458,8 @@ WaveTrackArray ProjectAudioManager::ChooseExistingRecordingTracks(
return {};
}
/*! @excsafety{Strong} -- For state of current project's tracks */
void ProjectAudioManager::OnRecord(bool altAppearance)
// STRONG-GUARANTEE (for state of current project's tracks)
{
bool bPreferNewTrack;
gPrefs->Read("/GUI/PreferNewTrackRecord", &bPreferNewTrack, false);
@ -803,10 +803,10 @@ void ProjectAudioManager::OnPause()
}
}
/*! @excsafety{Strong} -- For state of mCutPreviewTracks*/
void ProjectAudioManager::SetupCutPreviewTracks(double WXUNUSED(playStart), double cutStart,
double cutEnd, double WXUNUSED(playEnd))
// STRONG-GUARANTEE (for state of mCutPreviewTracks)
{
ClearCutPreviewTracks();
AudacityProject *p = &mProject;
@ -822,7 +822,7 @@ void ProjectAudioManager::SetupCutPreviewTracks(double WXUNUSED(playStart), doub
newTrack->Clear(cutStart, cutEnd);
cutPreviewTracks->Add( newTrack );
}
// use NOTHROW-GUARANTEE:
// use No-throw-guarantee:
mCutPreviewTracks = cutPreviewTracks;
}
}
@ -879,7 +879,7 @@ void ProjectAudioManager::OnAudioIOStopRecording()
}
else {
// Add to history
// We want this to have NOFAIL-GUARANTEE if we get here from exception
// We want this to have No-fail-guarantee if we get here from exception
// handling of recording, and that means we rely on the last autosave
// successully committed to the database, not risking a failure
history.PushState(XO("Recorded Audio"), XO("Record"),

View File

@ -132,8 +132,8 @@ namespace {
}
}
/*! @excsafety{Strong} */
bool Sequence::ConvertToSampleFormat(sampleFormat format)
// STRONG-GUARANTEE
{
if (format == mSampleFormat)
// no change
@ -467,8 +467,8 @@ namespace {
}
}
/*! @excsafety{Strong} */
void Sequence::Paste(sampleCount s, const Sequence *src)
// STRONG-GUARANTEE
{
if ((s < 0) || (s > mNumSamples))
{
@ -568,10 +568,10 @@ void Sequence::Paste(sampleCount s, const Sequence *src)
largerBlockLen.as_size_t(),
mSampleFormat);
// Don't make a duplicate array. We can still give STRONG-GUARANTEE
// Don't make a duplicate array. We can still give Strong-guarantee
// if we modify only one block in place.
// use NOFAIL-GUARANTEE in remaining steps
// use No-fail-guarantee in remaining steps
for (unsigned int i = b + 1; i < numBlocks; i++)
mBlock[i].start += addedLen;
@ -668,14 +668,14 @@ void Sequence::Paste(sampleCount s, const Sequence *src)
(newBlock, mNumSamples + addedLen, wxT("Paste branch three"));
}
/*! @excsafety{Strong} */
void Sequence::SetSilence(sampleCount s0, sampleCount len)
// STRONG-GUARANTEE
{
SetSamples(NULL, mSampleFormat, s0, len);
}
/*! @excsafety{Strong} */
void Sequence::InsertSilence(sampleCount s0, sampleCount len)
// STRONG-GUARANTEE
{
auto &factory = *mpFactory;
@ -720,7 +720,7 @@ void Sequence::InsertSilence(sampleCount s0, sampleCount len)
sTrack.mNumSamples = pos;
// use STRONG-GUARANTEE
// use Strong-guarantee
Paste(s0, &sTrack);
}
@ -1104,9 +1104,9 @@ bool Sequence::Get(int b, samplePtr buffer, sampleFormat format,
}
// Pass NULL to set silence
/*! @excsafety{Strong} */
void Sequence::SetSamples(samplePtr buffer, sampleFormat format,
sampleCount start, sampleCount len)
// STRONG-GUARANTEE
{
auto &factory = *mpFactory;
@ -1470,8 +1470,8 @@ size_t Sequence::GetIdealAppendLen() const
return max - lastBlockLen;
}
/*! @excsafety{Strong} */
void Sequence::Append(samplePtr buffer, sampleFormat format, size_t len)
// STRONG-GUARANTEE
{
if (len == 0)
return;
@ -1577,8 +1577,8 @@ void Sequence::Blockify(SampleBlockFactory &factory,
}
}
/*! @excsafety{Strong} */
void Sequence::Delete(sampleCount start, sampleCount len)
// STRONG-GUARANTEE
{
if (len == 0)
return;
@ -1631,10 +1631,10 @@ void Sequence::Delete(sampleCount start, sampleCount len)
b.sb = factory.Create(scratch.ptr(), newLen, mSampleFormat);
// Don't make a duplicate array. We can still give STRONG-GUARANTEE
// Don't make a duplicate array. We can still give Strong-guarantee
// if we modify only one block in place.
// use NOFAIL-GUARANTEE in remaining steps
// use No-fail-guarantee in remaining steps
for (unsigned int j = b0 + 1; j < numBlocks; j++)
mBlock[j].start -= len;
@ -1814,7 +1814,7 @@ void Sequence::CommitChangesIfConsistent
ConsistencyCheck( newBlock, mMaxSamples, 0, numSamples, whereStr ); // may throw
// now commit
// use NOFAIL-GUARANTEE
// use No-fail-guarantee
mBlock.swap(newBlock);
mNumSamples = numSamples;
@ -1857,7 +1857,7 @@ void Sequence::AppendBlocksIfConsistent
ConsistencyCheck( mBlock, mMaxSamples, prevSize, numSamples, whereStr ); // may throw
// now commit
// use NOFAIL-GUARANTEE
// use No-fail-guarantee
mNumSamples = numSamples;
consistent = true;

View File

@ -1073,8 +1073,8 @@ void TrackList::UpdatePendingTracks()
}
}
/*! @excsafety{No-fail} */
void TrackList::ClearPendingTracks( ListOfTracks *pAdded )
// NOFAIL-GUARANTEE
{
for (const auto &pTrack: mPendingUpdates)
pTrack->SetOwner( {}, {} );
@ -1115,6 +1115,7 @@ void TrackList::ClearPendingTracks( ListOfTracks *pAdded )
}
}
/*! @excsafety{Strong} */
bool TrackList::ApplyPendingTracks()
{
bool result = false;
@ -1128,8 +1129,8 @@ bool TrackList::ApplyPendingTracks()
updates.swap( mPendingUpdates );
}
// Remaining steps must be NOFAIL-GUARANTEE so that this function
// gives STRONG-GUARANTEE
// Remaining steps must be No-fail-guarantee so that this function
// gives Strong-guarantee
std::vector< std::shared_ptr<Track> > reinstated;

View File

@ -211,8 +211,8 @@ WaveClip::~WaveClip()
{
}
/*! @excsafety{No-fail} */
void WaveClip::SetOffset(double offset)
// NOFAIL-GUARANTEE
{
mOffset = offset;
mEnvelope->SetOffset(mOffset);
@ -224,14 +224,14 @@ bool WaveClip::GetSamples(samplePtr buffer, sampleFormat format,
return mSequence->Get(buffer, format, start, len, mayThrow);
}
/*! @excsafety{Strong} */
void WaveClip::SetSamples(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len)
// STRONG-GUARANTEE
{
// use STRONG-GUARANTEE
// use Strong-guarantee
mSequence->SetSamples(buffer, format, start, len);
// use NOFAIL-GUARANTEE
// use No-fail-guarantee
MarkChanged();
}
@ -1169,8 +1169,8 @@ void WaveClip::ConvertToSampleFormat(sampleFormat format)
MarkChanged();
}
/*! @excsafety{No-fail} */
void WaveClip::UpdateEnvelopeTrackLen()
// NOFAIL-GUARANTEE
{
mEnvelope->SetTrackLen
((mSequence->GetNumSamples().as_double()) / mRate, 1.0 / GetRate());
@ -1202,11 +1202,11 @@ void WaveClip::GetDisplayRect(wxRect* r)
*r = mDisplayRect;
}
/*! @excsafety{Partial}
-- Some prefix (maybe none) of the buffer is appended,
and no content already flushed to disk is lost. */
bool WaveClip::Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride /* = 1 */)
// PARTIAL-GUARANTEE in case of exceptions:
// Some prefix (maybe none) of the buffer is appended, and no content already
// flushed to disk is lost.
{
//wxLogDebug(wxT("Append: len=%lli"), (long long) len);
bool result = false;
@ -1219,7 +1219,7 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
mAppendBuffer.Allocate(maxBlockSize, seqFormat);
auto cleanup = finally( [&] {
// use NOFAIL-GUARANTEE
// use No-fail-guarantee
UpdateEnvelopeTrackLen();
MarkChanged();
} );
@ -1227,11 +1227,11 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
for(;;) {
if (mAppendBufferLen >= blockSize) {
// flush some previously appended contents
// use STRONG-GUARANTEE
// use Strong-guarantee
mSequence->Append(mAppendBuffer.ptr(), seqFormat, blockSize);
result = true;
// use NOFAIL-GUARANTEE for rest of this "if"
// use No-fail-guarantee for rest of this "if"
memmove(mAppendBuffer.ptr(),
mAppendBuffer.ptr() + blockSize * SAMPLE_SIZE(seqFormat),
(mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat));
@ -1242,7 +1242,7 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
if (len == 0)
break;
// use NOFAIL-GUARANTEE for rest of this "for"
// use No-fail-guarantee for rest of this "for"
wxASSERT(mAppendBufferLen <= maxBlockSize);
auto toCopy = std::min(len, maxBlockSize - mAppendBufferLen);
@ -1261,11 +1261,12 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
return result;
}
/*! @excsafety{Mixed} */
/*! @excsafety{No-fail} -- The clip will be in a flushed state. */
/*! @excsafety{Partial}
-- Some initial portion (maybe none) of the append buffer of the
clip gets appended; no previously flushed contents are lost. */
void WaveClip::Flush()
// NOFAIL-GUARANTEE that the clip will be in a flushed state.
// PARTIAL-GUARANTEE in case of exceptions:
// Some initial portion (maybe none) of the append buffer of the
// clip gets appended; no previously flushed contents are lost.
{
//wxLogDebug(wxT("WaveClip::Flush"));
//wxLogDebug(wxT(" mAppendBufferLen=%lli"), (long long) mAppendBufferLen);
@ -1277,7 +1278,7 @@ void WaveClip::Flush()
// Blow away the append buffer even in case of failure. May lose some
// data but don't leave the track in an un-flushed state.
// Use NOFAIL-GUARANTEE of these steps.
// Use No-fail-guarantee of these steps.
mAppendBufferLen = 0;
UpdateEnvelopeTrackLen();
MarkChanged();
@ -1366,8 +1367,8 @@ void WaveClip::WriteXML(XMLWriter &xmlFile) const
xmlFile.EndTag(wxT("waveclip"));
}
/*! @excsafety{Strong} */
void WaveClip::Paste(double t0, const WaveClip* other)
// STRONG-GUARANTEE
{
const bool clipNeedsResampling = other->mRate != mRate;
const bool clipNeedsNewFormat =
@ -1409,10 +1410,10 @@ void WaveClip::Paste(double t0, const WaveClip* other)
sampleCount s0;
TimeToSamplesClip(t0, &s0);
// Assume STRONG-GUARANTEE from Sequence::Paste
// Assume Strong-guarantee from Sequence::Paste
mSequence->Paste(s0, pastedClip->mSequence.get());
// Assume NOFAIL-GUARANTEE in the remaining
// Assume No-fail-guarantee in the remaining
MarkChanged();
auto sampleTime = 1.0 / GetRate();
mEnvelope->PasteEnvelope
@ -1423,17 +1424,17 @@ void WaveClip::Paste(double t0, const WaveClip* other)
mCutLines.push_back(std::move(holder));
}
/*! @excsafety{Strong} */
void WaveClip::InsertSilence( double t, double len, double *pEnvelopeValue )
// STRONG-GUARANTEE
{
sampleCount s0;
TimeToSamplesClip(t, &s0);
auto slen = (sampleCount)floor(len * mRate + 0.5);
// use STRONG-GUARANTEE
// use Strong-guarantee
GetSequence()->InsertSilence(s0, slen);
// use NOFAIL-GUARANTEE
// use No-fail-guarantee
OffsetCutLines(t, len);
const auto sampleTime = 1.0 / GetRate();
@ -1456,25 +1457,25 @@ void WaveClip::InsertSilence( double t, double len, double *pEnvelopeValue )
MarkChanged();
}
/*! @excsafety{Strong} */
void WaveClip::AppendSilence( double len, double envelopeValue )
// STRONG-GUARANTEE
{
auto t = GetEndTime();
InsertSilence( t, len, &envelopeValue );
}
/*! @excsafety{Strong} */
void WaveClip::Clear(double t0, double t1)
// STRONG-GUARANTEE
{
sampleCount s0, s1;
TimeToSamplesClip(t0, &s0);
TimeToSamplesClip(t1, &s1);
// use STRONG-GUARANTEE
// use Strong-guarantee
GetSequence()->Delete(s0, s1-s0);
// use NOFAIL-GUARANTEE in the remaining
// use No-fail-guarantee in the remaining
// msmeyer
//
@ -1524,10 +1525,10 @@ void WaveClip::Clear(double t0, double t1)
MarkChanged();
}
/*! @excsafety{Weak}
-- This WaveClip remains destructible in case of AudacityException.
But some cutlines may be deleted */
void WaveClip::ClearAndAddCutLine(double t0, double t1)
// WEAK-GUARANTEE
// this WaveClip remains destructible in case of AudacityException.
// But some cutlines may be deleted
{
if (t0 > GetEndTime() || t1 < GetStartTime())
return; // time out of bounds
@ -1565,7 +1566,7 @@ void WaveClip::ClearAndAddCutLine(double t0, double t1)
TimeToSamplesClip(t0, &s0);
TimeToSamplesClip(t1, &s1);
// use WEAK-GUARANTEE
// use Weak-guarantee
GetSequence()->Delete(s0, s1-s0);
// Collapse envelope
@ -1598,8 +1599,8 @@ bool WaveClip::FindCutLine(double cutLinePosition,
return false;
}
/*! @excsafety{Strong} */
void WaveClip::ExpandCutLine(double cutLinePosition)
// STRONG-GUARANTEE
{
auto end = mCutLines.end();
auto it = std::find_if( mCutLines.begin(), end,
@ -1609,7 +1610,7 @@ void WaveClip::ExpandCutLine(double cutLinePosition)
if ( it != end ) {
auto cutline = it->get();
// assume STRONG-GUARANTEE from Paste
// assume Strong-guarantee from Paste
// Envelope::Paste takes offset into account, WaveClip::Paste doesn't!
// Do this to get the right result:
@ -1646,8 +1647,8 @@ bool WaveClip::RemoveCutLine(double cutLinePosition)
return false;
}
/*! @excsafety{No-fail} */
void WaveClip::OffsetCutLines(double t0, double len)
// NOFAIL-GUARANTEE
{
for (const auto &cutLine : mCutLines)
{
@ -1671,8 +1672,8 @@ void WaveClip::SetRate(int rate)
MarkChanged();
}
/*! @excsafety{Strong} */
void WaveClip::Resample(int rate, ProgressDialog *progress)
// STRONG-GUARANTEE
{
// Note: it is not necessary to do this recursively to cutlines.
// They get resampled as needed when they are expanded.
@ -1744,7 +1745,7 @@ void WaveClip::Resample(int rate, ProgressDialog *progress)
};
else
{
// Use NOFAIL-GUARANTEE in these steps
// Use No-fail-guarantee in these steps
// Invalidate wave display cache
mWaveCache = std::make_unique<WaveCache>();

View File

@ -216,7 +216,8 @@ public:
int GetColourIndex( ) const { return mColourIndex;};
void SetOffset(double offset);
double GetOffset() const { return mOffset; }
void Offset(double delta) // NOFAIL-GUARANTEE
/*! @excsafety{No-fail} */
void Offset(double delta)
{ SetOffset(GetOffset() + delta); }
double GetStartTime() const;
double GetEndTime() const;
@ -251,7 +252,8 @@ public:
/** WaveTrack calls this whenever data in the wave clip changes. It is
* called automatically when WaveClip has a chance to know that something
* has changed, like when member functions SetSamples() etc. are called. */
void MarkChanged() // NOFAIL-GUARANTEE
/*! @excsafety{No-fail} */
void MarkChanged()
{ mDirty++; }
/** Getting high-level data for screen display and clipping

View File

@ -209,13 +209,13 @@ double WaveTrack::GetOffset() const
return GetStartTime();
}
/*! @excsafety{No-fail} */
void WaveTrack::SetOffset(double o)
// NOFAIL-GUARANTEE
{
double delta = o - GetOffset();
for (const auto &clip : mClips)
// assume NOFAIL-GUARANTEE
// assume No-fail-guarantee
clip->SetOffset(clip->GetOffset() + delta);
mOffset = o;
@ -404,8 +404,8 @@ void WaveTrack::SetOldChannelGain(int channel, float gain)
/*! @excsafety{Strong} */
void WaveTrack::SetWaveColorIndex(int colorIndex)
// STRONG-GUARANTEE
{
for (const auto &clip : mClips)
clip->SetColourIndex( colorIndex );
@ -413,9 +413,8 @@ void WaveTrack::SetWaveColorIndex(int colorIndex)
}
/*! @excsafety{Weak} -- Might complete on only some clips */
void WaveTrack::ConvertToSampleFormat(sampleFormat format)
// WEAK-GUARANTEE
// might complete on only some clips
{
for (const auto &clip : mClips)
clip->ConvertToSampleFormat(format);
@ -456,8 +455,8 @@ Track::Holder WaveTrack::Cut(double t0, double t1)
return tmp;
}
/*! @excsafety{Strong} */
Track::Holder WaveTrack::SplitCut(double t0, double t1)
// STRONG-GUARANTEE
{
if (t1 < t0)
THROW_INCONSISTENCY_EXCEPTION;
@ -489,8 +488,8 @@ Track::Holder WaveTrack::CutAndAddCutLine(double t0, double t1)
//Trim trims within a clip, rather than trimming everything.
//If a bound is outside a clip, it trims everything.
/*! @excsafety{Weak} */
void WaveTrack::Trim (double t0, double t1)
// WEAK-GUARANTEE
{
bool inside0 = false;
bool inside1 = false;
@ -611,14 +610,14 @@ Track::Holder WaveTrack::CopyNonconst(double t0, double t1)
return Copy(t0, t1);
}
/*! @excsafety{Strong} */
void WaveTrack::Clear(double t0, double t1)
// STRONG-GUARANTEE
{
HandleClear(t0, t1, false, false);
}
/*! @excsafety{Strong} */
void WaveTrack::ClearAndAddCutLine(double t0, double t1)
// STRONG-GUARANTEE
{
HandleClear(t0, t1, true, false);
}
@ -715,6 +714,8 @@ void WaveTrack::SetWaveformSettings(std::unique_ptr<WaveformSettings> &&pSetting
// be pasted with visible split lines. Normally, effects do not
// want these extra lines, so they may be merged out.
//
/*! @excsafety{Weak} -- This WaveTrack remains destructible in case of AudacityException.
But some of its cutline clips may have been destroyed. */
void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
double t1, // End of time to clear
const Track *src, // What to paste
@ -722,15 +723,12 @@ void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
bool merge, // Whether to remove 'extra' splits
const TimeWarper *effectWarper // How does time change
)
// WEAK-GUARANTEE
// this WaveTrack remains destructible in case of AudacityException.
// But some of its cutline clips may have been destroyed.
{
double dur = std::min(t1 - t0, src->GetEndTime());
// If duration is 0, then it's just a plain paste
if (dur == 0.0) {
// use WEAK-GUARANTEE
// use Weak-guarantee
Paste(t0, src);
return;
}
@ -836,7 +834,7 @@ void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
if (prev) {
// It must be that clip is what was pasted and it begins where
// prev ends.
// use WEAK-GUARANTEE
// use Weak-guarantee
MergeClips(GetClipIndex(prev), GetClipIndex(clip));
break;
}
@ -893,8 +891,8 @@ void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
}
}
/*! @excsafety{Strong} */
void WaveTrack::SplitDelete(double t0, double t1)
// STRONG-GUARANTEE
{
bool addCutLines = false;
bool split = true;
@ -956,9 +954,9 @@ void WaveTrack::AddClip(std::shared_ptr<WaveClip> &&clip)
mClips.push_back(std::move(clip)); // transfer ownership
}
/*! @excsafety{Strong} */
void WaveTrack::HandleClear(double t0, double t1,
bool addCutLines, bool split)
// STRONG-GUARANTEE
{
if (t1 < t0)
THROW_INCONSISTENCY_EXCEPTION;
@ -1067,7 +1065,7 @@ void WaveTrack::HandleClear(double t0, double t1,
}
// Only now, change the contents of this track
// use NOFAIL-GUARANTEE for the rest
// use No-fail-guarantee for the rest
for (const auto &clip : mClips)
{
@ -1133,8 +1131,8 @@ void WaveTrack::SyncLockAdjust(double oldT1, double newT1)
}
}
/*! @excsafety{Weak} */
void WaveTrack::Paste(double t0, const Track *src)
// WEAK-GUARANTEE
{
bool editClipCanMove = GetEditClipsCanMove();
@ -1244,7 +1242,7 @@ void WaveTrack::Paste(double t0, const Track *src)
if (clip->GetStartTime() > insideClip->GetStartTime() &&
insideClip->GetEndTime() + insertDuration >
clip->GetStartTime())
// STRONG-GUARANTEE in case of this path
// Strong-guarantee in case of this path
// not that it matters.
throw SimpleMessageBoxException{
XO("There is not enough room available to paste the selection")
@ -1264,7 +1262,7 @@ void WaveTrack::Paste(double t0, const Track *src)
//wxPrintf("paste: multi clip mode!\n");
if (!editClipCanMove && !IsEmpty(t0, t0+insertDuration-1.0/mRate))
// STRONG-GUARANTEE in case of this path
// Strong-guarantee in case of this path
// not that it matters.
throw SimpleMessageBoxException{
XO("There is not enough room available to paste the selection")
@ -1325,8 +1323,8 @@ void WaveTrack::Silence(double t0, double t1)
}
}
/*! @excsafety{Strong} */
void WaveTrack::InsertSilence(double t, double len)
// STRONG-GUARANTEE
{
// Nothing to do, if length is zero.
// Fixes Bug 1626
@ -1340,7 +1338,7 @@ void WaveTrack::InsertSilence(double t, double len)
// Special case if there is no clip yet
auto clip = std::make_unique<WaveClip>(mpFactory, mFormat, mRate, this->GetWaveColorIndex());
clip->InsertSilence(0, len);
// use NOFAIL-GUARANTEE
// use No-fail-guarantee
mClips.push_back( std::move( clip ) );
return;
}
@ -1350,11 +1348,11 @@ void WaveTrack::InsertSilence(double t, double len)
const auto it = std::find_if( mClips.begin(), end,
[&](const WaveClipHolder &clip) { return clip->WithinClip(t); } );
// use STRONG-GUARANTEE
// use Strong-guarantee
if (it != end)
it->get()->InsertSilence(t, len);
// use NOFAIL-GUARANTEE
// use No-fail-guarantee
for (const auto &clip : mClips)
{
if (clip->BeforeClip(t))
@ -1365,8 +1363,8 @@ void WaveTrack::InsertSilence(double t, double len)
//Performs the opposite of Join
//Analyses selected region for possible Joined clips and disjoins them
/*! @excsafety{Weak} */
void WaveTrack::Disjoin(double t0, double t1)
// WEAK-GUARANTEE
{
auto minSamples = TimeToLongSamples( WAVETRACK_MERGE_POINT_TOLERANCE );
const size_t maxAtOnce = 1048576;
@ -1443,8 +1441,8 @@ void WaveTrack::Disjoin(double t0, double t1)
}
}
/*! @excsafety{Weak} */
void WaveTrack::Join(double t0, double t1)
// WEAK-GUARANTEE
{
// Merge all WaveClips overlapping selection into one
@ -1497,11 +1495,11 @@ void WaveTrack::Join(double t0, double t1)
}
}
/*! @excsafety{Partial}
-- Some prefix (maybe none) of the buffer is appended,
and no content already flushed to disk is lost. */
bool WaveTrack::Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride /* = 1 */)
// PARTIAL-GUARANTEE in case of exceptions:
// Some prefix (maybe none) of the buffer is appended, and no content already
// flushed to disk is lost.
{
return RightmostOrNewClip()->Append(buffer, format, len, stride);
}
@ -1562,11 +1560,12 @@ size_t WaveTrack::GetIdealBlockSize()
return NewestOrNewClip()->GetSequence()->GetIdealBlockSize();
}
/*! @excsafety{Mixed} */
/*! @excsafety{No-fail} -- The rightmost clip will be in a flushed state. */
/*! @excsafety{Partial}
-- Some initial portion (maybe none) of the append buffer of the rightmost
clip gets appended; no previously saved contents are lost. */
void WaveTrack::Flush()
// NOFAIL-GUARANTEE that the rightmost clip will be in a flushed state.
// PARTIAL-GUARANTEE in case of exceptions:
// Some initial portion (maybe none) of the append buffer of the rightmost
// clip gets appended; no previously saved contents are lost.
{
// After appending, presumably. Do this to the clip that gets appended.
RightmostOrNewClip()->Flush();
@ -1929,9 +1928,9 @@ bool WaveTrack::Get(samplePtr buffer, sampleFormat format,
return result;
}
/*! @excsafety{Weak} */
void WaveTrack::Set(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len)
// WEAK-GUARANTEE
{
for (const auto &clip: mClips)
{
@ -2123,8 +2122,8 @@ WaveClip* WaveTrack::NewestOrNewClip()
return mClips.back().get();
}
/*! @excsafety{No-fail} */
WaveClip* WaveTrack::RightmostOrNewClip()
// NOFAIL-GUARANTEE
{
if (mClips.empty()) {
WaveClip *clip = CreateClip();
@ -2252,16 +2251,16 @@ bool WaveTrack::CanInsertClip(WaveClip* clip, double &slideBy, double &toleranc
return true;
}
/*! @excsafety{Weak} */
void WaveTrack::Split( double t0, double t1 )
// WEAK-GUARANTEE
{
SplitAt( t0 );
if( t0 != t1 )
SplitAt( t1 );
}
/*! @excsafety{Weak} */
void WaveTrack::SplitAt(double t)
// WEAK-GUARANTEE
{
for (const auto &c : mClips)
{
@ -2350,9 +2349,9 @@ void WaveTrack::UpdateLocationsCache() const
}
// Expand cut line (that is, re-insert audio, then DELETE audio saved in cut line)
/*! @excsafety{Strong} */
void WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
double* cutlineEnd)
// STRONG-GUARANTEE
{
bool editClipCanMove = GetEditClipsCanMove();
@ -2373,7 +2372,7 @@ void WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
{
if (clip2->GetStartTime() > clip->GetStartTime() &&
clip->GetEndTime() + end - start > clip2->GetStartTime())
// STRONG-GUARANTEE in case of this path
// Strong-guarantee in case of this path
throw SimpleMessageBoxException{
XO("There is not enough room available to expand the cut line")
};
@ -2382,7 +2381,7 @@ void WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
clip->ExpandCutLine(cutLinePosition);
// STRONG-GUARANTEE provided that the following gives NOFAIL-GUARANTEE
// Strong-guarantee provided that the following gives No-fail-guarantee
if (cutlineStart)
*cutlineStart = start;
@ -2410,8 +2409,8 @@ bool WaveTrack::RemoveCutLine(double cutLinePosition)
return false;
}
/*! @excsafety{Strong} */
void WaveTrack::MergeClips(int clipidx1, int clipidx2)
// STRONG-GUARANTEE
{
WaveClip* clip1 = GetClipByIndex(clipidx1);
WaveClip* clip2 = GetClipByIndex(clipidx2);
@ -2420,18 +2419,18 @@ void WaveTrack::MergeClips(int clipidx1, int clipidx2)
return; // Don't throw, just do nothing.
// Append data from second clip to first clip
// use STRONG-GUARANTEE
// use Strong-guarantee
clip1->Paste(clip1->GetEndTime(), clip2);
// use NOFAIL-GUARANTEE for the rest
// use No-fail-guarantee for the rest
// Delete second clip
auto it = FindClip(mClips, clip2);
mClips.erase(it);
}
/*! @excsafety{Weak} -- Partial completion may leave clips at differing sample rates!
*/
void WaveTrack::Resample(int rate, ProgressDialog *progress)
// WEAK-GUARANTEE
// Partial completion may leave clips at differing sample rates!
{
for (const auto &clip : mClips)
clip->Resample(rate, progress);

View File

@ -583,8 +583,8 @@ void ControlToolBar::PlayDefault()
ProjectAudioManager::Get( mProject ).PlayCurrentRegion(looped, cutPreview);
}
/*! @excsafety{Strong} -- For state of current project's tracks */
void ControlToolBar::OnRecord(wxCommandEvent &evt)
// STRONG-GUARANTEE (for state of current project's tracks)
{
// TODO: It would be neater if Menu items and Toolbar buttons used the same code for
// enabling/disabling, and all fell into the same action routines.