1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-16 08:34:10 +02:00

Translate exceptions to error codes in callback functions...

... That is what the library protocols allow, and libraries may be written
in C and might corrupt their state if C++ exceptions pass through them.
This commit is contained in:
Paul Licameli 2016-11-21 16:32:16 -05:00
parent 576d3e3013
commit 6525bb18cf
6 changed files with 173 additions and 139 deletions

View File

@ -91,6 +91,7 @@ scroll information. It also has some status flags.
#endif #endif
#endif #endif
#include "AudacityException.h"
#include "FreqWindow.h" #include "FreqWindow.h"
#include "effects/Contrast.h" #include "effects/Contrast.h"
#include "AutoRecovery.h" #include "AutoRecovery.h"
@ -446,19 +447,25 @@ public:
bool OnDropFiles(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxArrayString& filenames) override bool OnDropFiles(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxArrayString& filenames) override
{ {
//sort by OD non OD. load Non OD first so user can start editing asap. // Experiment shows that this function can be reached while there is no
wxArrayString sortednames(filenames); // catch block above in wxWidgets. So stop all exceptions here.
return GuardedCall< bool > ( [&] {
//sort by OD non OD. load Non OD first so user can start editing asap.
wxArrayString sortednames(filenames);
ODManager::Pauser pauser; ODManager::Pauser pauser;
sortednames.Sort(CompareNoCaseFileName); sortednames.Sort(CompareNoCaseFileName);
for (unsigned int i = 0; i < sortednames.GetCount(); i++) {
mProject->Import(sortednames[i]); for (unsigned int i = 0; i < sortednames.GetCount(); i++) {
}
mProject->HandleResize(); // Adjust scrollers for NEW track sizes.
return true; mProject->Import(sortednames[i]);
}
mProject->HandleResize(); // Adjust scrollers for NEW track sizes.
return true;
} );
} }
private: private:
@ -487,7 +494,14 @@ bool ImportXMLTagHandler::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
} }
WaveTrackArray trackArray; WaveTrackArray trackArray;
mProject->Import(strAttr, &trackArray);
// Guard this call so that C++ exceptions don't propagate through
// the expat library
GuardedCall< void >(
[&] { mProject->Import(strAttr, &trackArray); },
[&] (AudacityException*) { trackArray.clear(); }
);
if (trackArray.empty()) if (trackArray.empty())
return false; return false;

View File

@ -86,6 +86,7 @@ CommandManager. It holds the callback for one command.
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#include <wx/tokenzr.h> #include <wx/tokenzr.h>
#include "../AudacityException.h"
#include "../Prefs.h" #include "../Prefs.h"
#include "../Project.h" #include "../Project.h"
@ -190,47 +191,51 @@ public:
int FilterEvent(wxEvent& event) override int FilterEvent(wxEvent& event) override
{ {
// Quickly bail if this isn't something we want. // Unguarded exception propagation may crash the program, at least
wxEventType type = event.GetEventType(); // on Mac while in the objective-C closure above
if (type != wxEVT_CHAR_HOOK && type != wxEVT_KEY_UP) return GuardedCall< int > ( [&] {
{ // Quickly bail if this isn't something we want.
wxEventType type = event.GetEventType();
if (type != wxEVT_CHAR_HOOK && type != wxEVT_KEY_UP)
{
return Event_Skip;
}
// We must have a project since we will be working with the Command Manager
// and capture handler, both of which are (currently) tied to individual projects.
//
// Shouldn't they be tied to the application instead???
AudacityProject *project = GetActiveProject();
if (!project || !project->IsEnabled())
{
return Event_Skip;
}
// Make a copy of the event and (possibly) make it look like a key down
// event.
wxKeyEvent key = (wxKeyEvent &) event;
if (type == wxEVT_CHAR_HOOK)
{
key.SetEventType(wxEVT_KEY_DOWN);
}
// Give the capture handler first dibs at the event.
wxWindow *handler = project->GetKeyboardCaptureHandler();
if (handler && HandleCapture(handler, key))
{
return Event_Processed;
}
// Capture handler didn't want it, so ask the Command Manager.
CommandManager *manager = project->GetCommandManager();
if (manager && manager->FilterKeyEvent(project, key))
{
return Event_Processed;
}
// Give it back to WX for normal processing.
return Event_Skip; return Event_Skip;
} }, MakeSimpleGuard( Event_Skip ) );
// We must have a project since we will be working with the Command Manager
// and capture handler, both of which are (currently) tied to individual projects.
//
// Shouldn't they be tied to the application instead???
AudacityProject *project = GetActiveProject();
if (!project || !project->IsEnabled())
{
return Event_Skip;
}
// Make a copy of the event and (possibly) make it look like a key down
// event.
wxKeyEvent key = (wxKeyEvent &) event;
if (type == wxEVT_CHAR_HOOK)
{
key.SetEventType(wxEVT_KEY_DOWN);
}
// Give the capture handler first dibs at the event.
wxWindow *handler = project->GetKeyboardCaptureHandler();
if (handler && HandleCapture(handler, key))
{
return Event_Processed;
}
// Capture handler didn't want it, so ask the Command Manager.
CommandManager *manager = project->GetCommandManager();
if (manager && manager->FilterKeyEvent(project, key))
{
return Event_Processed;
}
// Give it back to WX for normal processing.
return Event_Skip;
} }
private: private:

View File

@ -45,6 +45,7 @@ effects from this one class.
#include <wx/numformatter.h> #include <wx/numformatter.h>
#include "../../AudacityApp.h" #include "../../AudacityApp.h"
#include "../../AudacityException.h"
#include "../../FileNames.h" #include "../../FileNames.h"
#include "../../Internat.h" #include "../../Internat.h"
#include "../../LabelTrack.h" #include "../../LabelTrack.h"
@ -1832,23 +1833,26 @@ int NyquistEffect::StaticPutCallback(float *buffer, int channel,
int NyquistEffect::PutCallback(float *buffer, int channel, int NyquistEffect::PutCallback(float *buffer, int channel,
long start, long len, long totlen) long start, long len, long totlen)
{ {
if (channel == 0) { // Don't let C++ exceptions propagate through the Nyquist library
double progress = mScale*((float)(start+len)/totlen); return GuardedCall<int>( [&] {
if (channel == 0) {
double progress = mScale*((float)(start+len)/totlen);
if (progress > mProgressOut) { if (progress > mProgressOut) {
mProgressOut = progress; mProgressOut = progress;
}
if (TotalProgress(mProgressIn+mProgressOut+mProgressTot)) {
return -1;
}
} }
if (TotalProgress(mProgressIn+mProgressOut+mProgressTot)) { if (mOutputTrack[channel]->Append((samplePtr)buffer, floatSample, len)) {
return -1; return 0; // success
} }
}
if (mOutputTrack[channel]->Append((samplePtr)buffer, floatSample, len)) { return -1; // failure
return 0; // success }, MakeSimpleGuard( -1 ) ); // translate all exceptions into failure
}
return -1; // failure
} }
void NyquistEffect::StaticOutputCallback(int c, void *This) void NyquistEffect::StaticOutputCallback(int c, void *This)

View File

@ -37,6 +37,7 @@
#include <wx/intl.h> // needed for _("translated stings") even if we #include <wx/intl.h> // needed for _("translated stings") even if we
// don't have libflac available // don't have libflac available
#include "../AudacityException.h"
#include "Import.h" #include "Import.h"
#include "ImportPlugin.h" #include "ImportPlugin.h"
@ -252,35 +253,38 @@ void MyFLACFile::error_callback(FLAC__StreamDecoderErrorStatus WXUNUSED(status))
FLAC__StreamDecoderWriteStatus MyFLACFile::write_callback(const FLAC__Frame *frame, FLAC__StreamDecoderWriteStatus MyFLACFile::write_callback(const FLAC__Frame *frame,
const FLAC__int32 * const buffer[]) const FLAC__int32 * const buffer[])
{ {
ArrayOf<short> tmp{ frame->header.blocksize }; // Don't let C++ exceptions propagate through libflac
return GuardedCall< FLAC__StreamDecoderWriteStatus > ( [&] {
auto tmp = ArrayOf< short >{ frame->header.blocksize };
auto iter = mFile->mChannels.begin(); auto iter = mFile->mChannels.begin();
for (unsigned int chn=0; chn<mFile->mNumChannels; ++iter, ++chn) { for (unsigned int chn=0; chn<mFile->mNumChannels; ++iter, ++chn) {
if (frame->header.bits_per_sample == 16) { if (frame->header.bits_per_sample == 16) {
for (unsigned int s=0; s<frame->header.blocksize; s++) { for (unsigned int s=0; s<frame->header.blocksize; s++) {
tmp[s]=buffer[chn][s]; tmp[s]=buffer[chn][s];
}
iter->get()->Append((samplePtr)tmp.get(),
int16Sample,
frame->header.blocksize);
}
else {
iter->get()->Append((samplePtr)buffer[chn],
int24Sample,
frame->header.blocksize);
} }
iter->get()->Append((samplePtr)tmp.get(),
int16Sample,
frame->header.blocksize);
} }
else {
iter->get()->Append((samplePtr)buffer[chn], mFile->mSamplesDone += frame->header.blocksize;
int24Sample,
frame->header.blocksize); mFile->mUpdateResult = mFile->mProgress->Update((wxULongLong_t) mFile->mSamplesDone, mFile->mNumSamples != 0 ? (wxULongLong_t)mFile->mNumSamples : 1);
if (mFile->mUpdateResult != ProgressResult::Success)
{
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
} }
}
mFile->mSamplesDone += frame->header.blocksize; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}, MakeSimpleGuard(FLAC__STREAM_DECODER_WRITE_STATUS_ABORT) );
mFile->mUpdateResult = mFile->mProgress->Update((wxULongLong_t) mFile->mSamplesDone, mFile->mNumSamples != 0 ? (wxULongLong_t)mFile->mNumSamples : 1);
if (mFile->mUpdateResult != ProgressResult::Success)
{
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
} }

View File

@ -46,6 +46,7 @@ Licensed under the GNU General Public License v2 or later
#endif #endif
// all the includes live here by default // all the includes live here by default
#include "../AudacityException.h"
#include "../SampleFormat.h" #include "../SampleFormat.h"
#include "../Tags.h" #include "../Tags.h"
#include "../Internat.h" #include "../Internat.h"
@ -489,20 +490,23 @@ inline void GstSampleUnref(GstSample *p) { gst_sample_unref(p); } // I can't use
static GstFlowReturn static GstFlowReturn
GStreamerNewSample(GstAppSink *appsink, gpointer data) GStreamerNewSample(GstAppSink *appsink, gpointer data)
{ {
GStreamerImportFileHandle *handle = (GStreamerImportFileHandle *)data; // Don't let C++ exceptions propagate through GStreamer
static GMutex mutex; return GuardedCall< GstFlowReturn > ( [&] {
GStreamerImportFileHandle *handle = (GStreamerImportFileHandle *)data;
static GMutex mutex;
// Get the sample // Get the sample
std::unique_ptr < GstSample, Deleter< GstSample, GstSampleUnref> > std::unique_ptr < GstSample, Deleter< GstSample, GstSampleUnref> >
sample{ gst_app_sink_pull_sample(appsink) }; sample{ gst_app_sink_pull_sample(appsink) };
// We must single thread here to prevent concurrent use of the // We must single thread here to prevent concurrent use of the
// Audacity track functions. // Audacity track functions.
g_mutex_locker locker{ mutex }; g_mutex_locker locker{ mutex };
handle->OnNewSample(GETCTX(appsink), sample.get()); handle->OnNewSample(GETCTX(appsink), sample.get());
return GST_FLOW_OK; return GST_FLOW_OK;
}, MakeSimpleGuard(GST_FLOW_ERROR) );
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -39,6 +39,7 @@
#include <wx/defs.h> #include <wx/defs.h>
#include <wx/intl.h> #include <wx/intl.h>
#include "../AudacityException.h"
#include "../Prefs.h" #include "../Prefs.h"
#include "Import.h" #include "Import.h"
#include "ImportPlugin.h" #include "ImportPlugin.h"
@ -480,59 +481,61 @@ enum mad_flow output_cb(void *_data,
struct mad_header const * WXUNUSED(header), struct mad_header const * WXUNUSED(header),
struct mad_pcm *pcm) struct mad_pcm *pcm)
{ {
int samplerate; // Don't C++ exceptions propagate through mad
struct private_data *data = (struct private_data *)_data; return GuardedCall< mad_flow > ( [&] {
int samplerate;
struct private_data *data = (struct private_data *)_data;
samplerate= pcm->samplerate; samplerate= pcm->samplerate;
auto channels = pcm->channels; auto channels = pcm->channels;
const auto samples = pcm->length; const auto samples = pcm->length;
/* If this is the first run, we need to create the WaveTracks that /* If this is the first run, we need to create the WaveTracks that
* will hold the data. We do this now because now is the first * will hold the data. We do this now because now is the first
* moment when we know how many channels there are. */ * moment when we know how many channels there are. */
if(data->channels.empty()) { if(data->channels.empty()) {
data->channels.resize(channels); data->channels.resize(channels);
sampleFormat format = (sampleFormat) gPrefs-> sampleFormat format = (sampleFormat) gPrefs->
Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample); Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
for(auto &channel: data->channels) { for(auto &channel: data->channels) {
channel = data->trackFactory->NewWaveTrack(format, samplerate); channel = data->trackFactory->NewWaveTrack(format, samplerate);
channel->SetChannel(Track::MonoChannel); channel->SetChannel(Track::MonoChannel);
}
/* special case: 2 channels is understood to be stereo */
if(channels == 2) {
data->channels.begin()->get()->SetChannel(Track::LeftChannel);
data->channels.rbegin()->get()->SetChannel(Track::RightChannel);
data->channels.begin()->get()->SetLinked(true);
}
data->numChannels = channels;
}
else {
// This is not the first run, protect us from libmad glitching
// on the number of channels
channels = data->numChannels;
} }
/* special case: 2 channels is understood to be stereo */ /* TODO: get rid of this by adding fixed-point support to SampleFormat.
if(channels == 2) { * For now, we allocate temporary float buffers to convert the fixed
data->channels.begin()->get()->SetChannel(Track::LeftChannel); * point samples into something we can feed to the WaveTrack. Allocating
data->channels.rbegin()->get()->SetChannel(Track::RightChannel); * big blocks of data like this isn't a great idea, but it's temporary.
data->channels.begin()->get()->SetLinked(true); */
} FloatBuffers channelBuffers{ channels, samples };
data->numChannels = channels; for(size_t smpl = 0; smpl < samples; smpl++)
} for(int chn = 0; chn < channels; chn++)
else { channelBuffers[chn][smpl] = scale(pcm->samples[chn][smpl]);
// This is not the first run, protect us from libmad glitching
// on the number of channels
channels = data->numChannels;
}
/* TODO: get rid of this by adding fixed-point support to SampleFormat.
* For now, we allocate temporary float buffers to convert the fixed
* point samples into something we can feed to the WaveTrack. Allocating
* big blocks of data like this isn't a great idea, but it's temporary.
*/
FloatBuffers channelBuffers{ channels, samples };
for (size_t smpl = 0; smpl < samples; smpl++)
for(int chn = 0; chn < channels; chn++) for(int chn = 0; chn < channels; chn++)
channelBuffers[chn][smpl] = scale(pcm->samples[chn][smpl]); data->channels[chn]->Append((samplePtr)channelBuffers[chn].get(),
floatSample,
samples);
for (int chn = 0; chn < channels; chn++) return MAD_FLOW_CONTINUE;
data->channels[chn]->Append((samplePtr)channelBuffers[chn].get(), }, MakeSimpleGuard(MAD_FLOW_BREAK) );
floatSample,
samples);
return MAD_FLOW_CONTINUE;
} }
enum mad_flow error_cb(void * WXUNUSED(_data), struct mad_stream * WXUNUSED(stream), enum mad_flow error_cb(void * WXUNUSED(_data), struct mad_stream * WXUNUSED(stream),