13
0

massive changes to clean up what happens during session destruction when an exception is thrown

git-svn-id: svn://localhost/ardour2/trunk@1261 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2007-01-03 18:08:11 +00:00
parent b5f497c0c9
commit ca81401b14
13 changed files with 227 additions and 144 deletions

View File

@ -1262,14 +1262,6 @@ ARDOUR_UI::do_engine_start ()
engine->start();
}
catch (AudioEngine::PortRegistrationFailure& err) {
engine->stop ();
error << _("Unable to create all required ports")
<< endmsg;
unload_session ();
return -1;
}
catch (...) {
engine->stop ();
error << _("Unable to start the session running")
@ -1588,7 +1580,7 @@ ARDOUR_UI::save_template ()
}
void
ARDOUR_UI::new_session (bool startup, std::string predetermined_path)
ARDOUR_UI::new_session (std::string predetermined_path)
{
string session_name;
string session_path;
@ -1818,6 +1810,14 @@ This prevents the session from being loaded."));
new_session = new Session (*engine, path, snap_name, mix_template);
}
/* handle this one in a different way than all others, so that its clear what happened */
catch (AudioEngine::PortRegistrationFailure& err) {
error << _("Unable to create all required ports")
<< endmsg;
return -1;
}
catch (...) {
error << string_compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name) << endmsg;
@ -2330,7 +2330,7 @@ ARDOUR_UI::cmdline_new_session (string path)
path = str;
}
new_session (false, path);
new_session (path);
_will_create_new_session_automatically = false; /* done it */
return FALSE; /* don't call it again */

View File

@ -128,7 +128,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI
_will_create_new_session_automatically = yn;
}
void new_session(bool startup = false, std::string path = string());
void new_session(std::string path = string());
gint cmdline_new_session (string path);
int unload_session ();
void close_session();

View File

@ -92,7 +92,7 @@ ARDOUR_UI::install_actions ()
/* the real actions */
act = ActionManager::register_action (main_actions, X_("New"), _("New"), bind (mem_fun(*this, &ARDOUR_UI::new_session), false, string ()));
act = ActionManager::register_action (main_actions, X_("New"), _("New"), bind (mem_fun(*this, &ARDOUR_UI::new_session), string ()));
ActionManager::register_action (main_actions, X_("Open"), _("Open"), mem_fun(*this, &ARDOUR_UI::open_session));
ActionManager::register_action (main_actions, X_("Recent"), _("Recent"), mem_fun(*this, &ARDOUR_UI::open_recent_session));

View File

@ -79,8 +79,6 @@ shutdown (int status)
} else {
if (ui) {
msg = _("stopping user interface\n");
write (1, msg, strlen (msg));
ui->kill();
}
@ -301,7 +299,7 @@ maybe_load_session ()
if (!session_name.length()) {
ui->hide_splash ();
if (!Config->get_no_new_session_dialog()) {
ui->new_session (true);
ui->new_session ();
}
return true;
@ -326,7 +324,10 @@ To create it from the command line, start ardour as \"ardour --new %1"), path) <
return false;
}
ui->load_session (path, name);
if (ui->load_session (path, name)) {
/* it failed */
return false;
}
} else {
@ -338,7 +339,7 @@ To create it from the command line, start ardour as \"ardour --new %1"), path) <
/* Show the NSD */
ui->hide_splash ();
if (!Config->get_no_new_session_dialog()) {
ui->new_session (true);
ui->new_session ();
}
}

View File

@ -173,33 +173,39 @@ class AudioDiskstream : public Diskstream
private:
struct ChannelInfo {
Sample *playback_wrap_buffer;
Sample *capture_wrap_buffer;
Sample *speed_buffer;
float peak_power;
boost::shared_ptr<AudioFileSource> fades_source;
boost::shared_ptr<AudioFileSource> write_source;
ChannelInfo ();
~ChannelInfo ();
Port *source;
Sample *current_capture_buffer;
Sample *current_playback_buffer;
void init (nframes_t buffer_size, nframes_t speed_buffer_size, nframes_t wrap_buffer_size);
void release ();
RingBufferNPT<Sample> *playback_buf;
RingBufferNPT<Sample> *capture_buf;
Sample* scrub_buffer;
Sample* scrub_forward_buffer;
Sample* scrub_reverse_buffer;
RingBufferNPT<Sample>::rw_vector playback_vector;
RingBufferNPT<Sample>::rw_vector capture_vector;
RingBufferNPT<CaptureTransition> * capture_transition_buf;
// the following are used in the butler thread only
nframes_t curr_capture_cnt;
Sample *playback_wrap_buffer;
Sample *capture_wrap_buffer;
Sample *speed_buffer;
float peak_power;
boost::shared_ptr<AudioFileSource> fades_source;
boost::shared_ptr<AudioFileSource> write_source;
Port *source;
Sample *current_capture_buffer;
Sample *current_playback_buffer;
RingBufferNPT<Sample> *playback_buf;
RingBufferNPT<Sample> *capture_buf;
Sample* scrub_buffer;
Sample* scrub_forward_buffer;
Sample* scrub_reverse_buffer;
RingBufferNPT<Sample>::rw_vector playback_vector;
RingBufferNPT<Sample>::rw_vector capture_vector;
RingBufferNPT<CaptureTransition> * capture_transition_buf;
// the following are used in the butler thread only
nframes_t curr_capture_cnt;
};
/* The two central butler operations */

View File

@ -197,8 +197,7 @@ class AudioEngine : public sigc::trackable
jack_client_t *_jack;
std::string jack_client_name;
Glib::Mutex _process_lock;
Glib::Mutex session_remove_lock;
Glib::Cond session_removed;
Glib::Cond session_removed;
bool session_remove_pending;
bool _running;
bool _has_run;

View File

@ -199,13 +199,13 @@ class IO : public PBD::StatefulDestructible
static void update_meters();
private:
private:
static sigc::signal<void> Meter;
static Glib::StaticMutex m_meter_signal_lock;
sigc::connection m_meter_connection;
public:
public:
/* automation */

View File

@ -933,6 +933,7 @@ class Session : public PBD::StatefulDestructible
private:
int create (bool& new_session, string* mix_template, nframes_t initial_length);
void destroy ();
nframes_t compute_initial_length ();

View File

@ -96,34 +96,6 @@ AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
}
}
void
AudioDiskstream::init_channel (ChannelInfo &chan)
{
chan.playback_wrap_buffer = 0;
chan.capture_wrap_buffer = 0;
chan.speed_buffer = 0;
chan.peak_power = 0.0f;
chan.source = 0;
chan.current_capture_buffer = 0;
chan.current_playback_buffer = 0;
chan.curr_capture_cnt = 0;
chan.playback_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size());
chan.capture_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size());
chan.capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
/* touch the ringbuffer buffers, which will cause
them to be mapped into locked physical RAM if
we're running with mlockall(). this doesn't do
much if we're not.
*/
memset (chan.playback_buf->buffer(), 0, sizeof (Sample) * chan.playback_buf->bufsize());
memset (chan.capture_buf->buffer(), 0, sizeof (Sample) * chan.capture_buf->bufsize());
memset (chan.capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * chan.capture_transition_buf->bufsize());
}
void
AudioDiskstream::init (Diskstream::Flag f)
{
@ -141,44 +113,19 @@ AudioDiskstream::init (Diskstream::Flag f)
assert(_n_channels == 1);
}
void
AudioDiskstream::destroy_channel (ChannelInfo &chan)
{
if (chan.write_source) {
chan.write_source.reset ();
}
if (chan.speed_buffer) {
delete [] chan.speed_buffer;
}
if (chan.playback_wrap_buffer) {
delete [] chan.playback_wrap_buffer;
}
if (chan.capture_wrap_buffer) {
delete [] chan.capture_wrap_buffer;
}
delete chan.playback_buf;
delete chan.capture_buf;
delete chan.capture_transition_buf;
chan.playback_buf = 0;
chan.capture_buf = 0;
}
AudioDiskstream::~AudioDiskstream ()
{
notify_callbacks ();
{
/* don't be holding this lock as we exit the destructor, glib will wince
visibly since the mutex gets destroyed before we release it.
*/
Glib::Mutex::Lock lm (state_lock);
for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan)
destroy_channel((*chan));
for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
(*chan).release ();
}
channels.clear();
}
}
@ -2107,15 +2054,19 @@ AudioDiskstream::add_channel ()
{
/* XXX need to take lock??? */
ChannelInfo chan;
/* this copies the ChannelInfo, which currently has no buffers. kind
of pointless really, but we want the channels list to contain
actual objects, not pointers to objects. mostly for convenience,
which isn't much in evidence.
*/
init_channel (chan);
channels.push_back (ChannelInfo());
chan.speed_buffer = new Sample[speed_buffer_size];
chan.playback_wrap_buffer = new Sample[wrap_buffer_size];
chan.capture_wrap_buffer = new Sample[wrap_buffer_size];
/* now allocate the buffers */
channels.push_back (chan);
channels.back().init (_session.diskstream_buffer_size(),
speed_buffer_size,
wrap_buffer_size);
_n_channels = channels.size();
@ -2127,10 +2078,8 @@ AudioDiskstream::remove_channel ()
{
if (channels.size() > 1) {
/* XXX need to take lock??? */
ChannelInfo & chan = channels.back();
destroy_channel (chan);
channels.back().release ();
channels.pop_back();
_n_channels = channels.size();
return 0;
}
@ -2310,3 +2259,82 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
requires_bounce = false;
return true;
}
AudioDiskstream::ChannelInfo::ChannelInfo ()
{
playback_wrap_buffer = 0;
capture_wrap_buffer = 0;
speed_buffer = 0;
peak_power = 0.0f;
source = 0;
current_capture_buffer = 0;
current_playback_buffer = 0;
curr_capture_cnt = 0;
playback_buf = 0;
capture_buf = 0;
capture_transition_buf = 0;
}
void
AudioDiskstream::ChannelInfo::init (nframes_t bufsize, nframes_t speed_size, nframes_t wrap_size)
{
speed_buffer = new Sample[speed_size];
playback_wrap_buffer = new Sample[wrap_size];
capture_wrap_buffer = new Sample[wrap_size];
playback_buf = new RingBufferNPT<Sample> (bufsize);
capture_buf = new RingBufferNPT<Sample> (bufsize);
capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
/* touch the ringbuffer buffers, which will cause
them to be mapped into locked physical RAM if
we're running with mlockall(). this doesn't do
much if we're not.
*/
memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize());
memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize());
memset (capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * capture_transition_buf->bufsize());
}
AudioDiskstream::ChannelInfo::~ChannelInfo ()
{
}
void
AudioDiskstream::ChannelInfo::release ()
{
if (write_source) {
write_source.reset ();
}
if (speed_buffer) {
delete [] speed_buffer;
speed_buffer = 0;
}
if (playback_wrap_buffer) {
delete [] playback_wrap_buffer;
playback_wrap_buffer = 0;
}
if (capture_wrap_buffer) {
delete [] capture_wrap_buffer;
capture_wrap_buffer = 0;
}
if (playback_buf) {
delete playback_buf;
playback_buf = 0;
}
if (capture_buf) {
delete capture_buf;
capture_buf = 0;
}
if (capture_transition_buf) {
delete capture_transition_buf;
capture_transition_buf = 0;
}
}

View File

@ -45,6 +45,12 @@ using namespace PBD;
gint AudioEngine::m_meter_exit;
static void
ardour_jack_error (const char* msg)
{
error << "JACK: " << msg << endmsg;
}
AudioEngine::AudioEngine (string client_name)
: ports (new Ports)
{
@ -76,11 +82,16 @@ AudioEngine::AudioEngine (string client_name)
AudioEngine::~AudioEngine ()
{
if (_running) {
jack_client_close (_jack);
{
Glib::Mutex::Lock tm (_process_lock);
session_removed.signal ();
if (_running) {
jack_client_close (_jack);
}
stop_metering_thread ();
}
stop_metering_thread ();
}
void
@ -407,8 +418,26 @@ AudioEngine::meter_thread ()
void
AudioEngine::set_session (Session *s)
{
Glib::Mutex::Lock pl (_process_lock);
if (!session) {
session = s;
nframes_t blocksize = jack_get_buffer_size (_jack);
/* page in as much of the session process code as we
can before we really start running.
*/
session->process (blocksize);
session->process (blocksize);
session->process (blocksize);
session->process (blocksize);
session->process (blocksize);
session->process (blocksize);
session->process (blocksize);
session->process (blocksize);
}
}
@ -422,12 +451,10 @@ AudioEngine::remove_session ()
if (session) {
session_remove_pending = true;
session_removed.wait(_process_lock);
}
}
} else {
session = 0;
}
remove_all_ports ();
@ -461,8 +488,6 @@ AudioEngine::register_input_port (DataType type, const string& portname)
return newport;
} else {
_process_lock.unlock();
throw PortRegistrationFailure();
}
@ -501,8 +526,6 @@ AudioEngine::register_output_port (DataType type, const string& portname)
return newport;
} else {
_process_lock.unlock();
throw PortRegistrationFailure ();
}
@ -1015,6 +1038,8 @@ AudioEngine::connect_to_jack (string client_name)
if (status & JackNameNotUnique) {
jack_client_name = jack_get_client_name (_jack);
}
jack_set_error_function (ardour_jack_error);
return 0;
}

View File

@ -53,6 +53,7 @@ extern "C" int isnan (double);
extern "C" int isinf (double);
#endif
#define BLOCK_PROCESS_CALLBACK() Glib::Mutex::Lock em (_session.engine().process_lock())
using namespace std;
using namespace ARDOUR;
@ -624,7 +625,7 @@ IO::disconnect_input (Port* our_port, string other_port, void* src)
}
{
Glib::Mutex::Lock em (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -660,7 +661,7 @@ IO::connect_input (Port* our_port, string other_port, void* src)
}
{
Glib::Mutex::Lock em(_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -694,7 +695,7 @@ IO::disconnect_output (Port* our_port, string other_port, void* src)
}
{
Glib::Mutex::Lock em(_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -727,7 +728,8 @@ IO::connect_output (Port* our_port, string other_port, void* src)
}
{
Glib::Mutex::Lock em(_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -786,7 +788,8 @@ IO::remove_output_port (Port* port, void* src)
IOChange change (NoChange);
{
Glib::Mutex::Lock em(_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -844,7 +847,8 @@ IO::add_output_port (string destination, void* src, DataType type)
type = _default_type;
{
Glib::Mutex::Lock em(_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -896,7 +900,8 @@ IO::remove_input_port (Port* port, void* src)
IOChange change (NoChange);
{
Glib::Mutex::Lock em(_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -956,7 +961,7 @@ IO::add_input_port (string source, void* src, DataType type)
type = _default_type;
{
Glib::Mutex::Lock em (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -1008,7 +1013,7 @@ int
IO::disconnect_inputs (void* src)
{
{
Glib::Mutex::Lock em (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -1028,7 +1033,7 @@ int
IO::disconnect_outputs (void* src)
{
{
Glib::Mutex::Lock em (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
{
Glib::Mutex::Lock lm (io_lock);
@ -1090,7 +1095,7 @@ IO::ensure_inputs_locked (uint32_t n, bool clear, void* src)
setup_peak_meters ();
reset_panner ();
/* pass it on */
throw err;
throw AudioEngine::PortRegistrationFailure();
}
_inputs.push_back (input_port);
@ -1140,7 +1145,7 @@ IO::ensure_io (uint32_t nin, uint32_t nout, bool clear, void* src)
}
{
Glib::Mutex::Lock em (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock lm (io_lock);
Port* port;
@ -1195,7 +1200,7 @@ IO::ensure_io (uint32_t nin, uint32_t nout, bool clear, void* src)
setup_peak_meters ();
reset_panner ();
/* pass it on */
throw err;
throw AudioEngine::PortRegistrationFailure();
}
_inputs.push_back (port);
@ -1228,7 +1233,7 @@ IO::ensure_io (uint32_t nin, uint32_t nout, bool clear, void* src)
setup_peak_meters ();
reset_panner ();
/* pass it on */
throw err;
throw AudioEngine::PortRegistrationFailure ();
}
_outputs.push_back (port);
@ -1289,7 +1294,7 @@ IO::ensure_inputs (uint32_t n, bool clear, bool lockit, void* src)
}
if (lockit) {
Glib::Mutex::Lock em (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock im (io_lock);
changed = ensure_inputs_locked (n, clear, src);
} else {
@ -1391,7 +1396,7 @@ IO::ensure_outputs (uint32_t n, bool clear, bool lockit, void* src)
/* XXX caller should hold io_lock, but generally doesn't */
if (lockit) {
Glib::Mutex::Lock em (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock im (io_lock);
changed = ensure_outputs_locked (n, clear, src);
} else {
@ -2191,7 +2196,7 @@ IO::use_input_connection (Connection& c, void* src)
uint32_t limit;
{
Glib::Mutex::Lock lm (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock lm2 (io_lock);
limit = c.nports();
@ -2269,7 +2274,7 @@ IO::use_output_connection (Connection& c, void* src)
uint32_t limit;
{
Glib::Mutex::Lock lm (_session.engine().process_lock());
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock lm2 (io_lock);
limit = c.nports();

View File

@ -281,12 +281,13 @@ Session::Session (AudioEngine &eng,
if (new_session) {
if (create (new_session, mix_template, compute_initial_length())) {
cerr << "create failed\n";
destroy ();
throw failed_constructor ();
}
}
if (second_stage_init (new_session)) {
cerr << "2nd state failed\n";
destroy ();
throw failed_constructor ();
}
@ -346,6 +347,7 @@ Session::Session (AudioEngine &eng,
if (new_session) {
if (create (new_session, 0, initial_length)) {
destroy ();
throw failed_constructor ();
}
}
@ -373,6 +375,7 @@ Session::Session (AudioEngine &eng,
Config->set_output_auto_connect (output_ac);
if (second_stage_init (new_session)) {
destroy ();
throw failed_constructor ();
}
@ -388,6 +391,12 @@ Session::Session (AudioEngine &eng,
}
Session::~Session ()
{
destroy ();
}
void
Session::destroy ()
{
/* if we got to here, leaving pending capture state around
is a mistake.
@ -2930,6 +2939,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool
if (cnt > limit) {
error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg;
destroy ();
throw failed_constructor();
}
}

View File

@ -99,12 +99,14 @@ void
Session::first_stage_init (string fullpath, string snapshot_name)
{
if (fullpath.length() == 0) {
destroy ();
throw failed_constructor();
}
char buf[PATH_MAX+1];
if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
error << string_compose(_("Could not use path %1 (%s)"), buf, strerror(errno)) << endmsg;
destroy ();
throw failed_constructor();
}
@ -155,7 +157,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
_worst_output_latency = 0;
_worst_input_latency = 0;
_worst_track_latency = 0;
_state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading);
_state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading|Deletion);
_slave = 0;
butler_mixdown_buffer = 0;
butler_gain_buffer = 0;
@ -307,7 +309,13 @@ Session::second_stage_init (bool new_session)
_engine.Halted.connect (mem_fun (*this, &Session::engine_halted));
_engine.Xrun.connect (mem_fun (*this, &Session::xrun_recovery));
when_engine_running();
try {
when_engine_running();
}
catch (...) {
return -1;
}
send_full_time_code ();
_engine.transport_locate (0);