13
0

new scheme for managing port deletion

shared_ptr<Port> now uses a deleter functor which pushes Port* to a lock-free FIFO so that the Port is
always deleted (and thus unregistered with the PortEngine/backend) in a safe context w.r.t. various
callbacks in the host. Currently the auto_connect_thread in Session has been tasked with doing these
deletions.
This commit is contained in:
Paul Davis 2016-10-13 17:18:42 -04:00
parent 1552547f65
commit 8f9a9523d2
7 changed files with 76 additions and 12 deletions

View File

@ -247,6 +247,8 @@ class LIBARDOUR_API AudioEngine : public PortManager, public SessionHandlePtr
PBD::Signal0<void> BecameSilent;
void reset_silence_countdown ();
void add_pending_port_deletion (Port*);
private:
AudioEngine ();

View File

@ -30,6 +30,7 @@
#include <boost/shared_ptr.hpp>
#include "pbd/rcu.h"
#include "pbd/ringbuffer.h"
#include "ardour/chan_count.h"
#include "ardour/midiport_manager.h"
@ -96,6 +97,9 @@ class LIBARDOUR_API PortManager
int get_ports (DataType, PortList&);
void remove_all_ports ();
void clear_pending_port_deletions ();
virtual void add_pending_port_deletion (Port*) = 0;
RingBuffer<Port*>& port_deletions_pending () { return _port_deletions_pending; }
/* per-Port monitoring */
@ -141,6 +145,7 @@ class LIBARDOUR_API PortManager
boost::shared_ptr<AudioBackend> _backend;
SerializedRCUManager<Ports> ports;
bool _port_remove_in_progress;
RingBuffer<Port*> _port_deletions_pending;
boost::shared_ptr<Port> register_port (DataType type, const std::string& portname, bool input, bool async = false, PortFlags extra_flags = PortFlags (0));
void port_registration_failure (const std::string& portname);

View File

@ -1149,6 +1149,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
VCAManager& vca_manager() { return *_vca_manager; }
void auto_connect_thread_wakeup ();
protected:
friend class AudioEngine;
void set_block_size (pframes_t nframes);

View File

@ -1481,3 +1481,19 @@ AudioEngine::set_latency_input_port (const string& name)
{
_latency_input_name = name;
}
void
AudioEngine::add_pending_port_deletion (Port* p)
{
if (_session) {
std::cerr << "Adding " << p->name() << " to pending port deletion list\n";
if (_port_deletions_pending.write (&p, 1) != 1) {
error << string_compose (_("programming error: port %1 could not be placed on the pending deletion queue\n"), p->name()) << endmsg;
}
_session->auto_connect_thread_wakeup ();
} else {
std::cerr << "Directly delete port\n";
delete p;
}
}

View File

@ -49,7 +49,8 @@ MidiPort::MidiPort (const std::string& name, PortFlags flags)
MidiPort::~MidiPort()
{
if (_shadow_port) {
_shadow_port->disconnect_all ();
AudioEngine::instance()->unregister_port (_shadow_port);
_shadow_port.reset ();
}
delete _buffer;

View File

@ -47,9 +47,20 @@ using std::vector;
PortManager::PortManager ()
: ports (new Ports)
, _port_remove_in_progress (false)
, _port_deletions_pending (8192) /* ick, arbitrary sizing */
{
}
void
PortManager::clear_pending_port_deletions ()
{
Port* p;
while (_port_deletions_pending.read (&p, 1) == 1) {
delete p;
}
}
void
PortManager::remove_all_ports ()
{
@ -72,6 +83,13 @@ PortManager::remove_all_ports ()
ports.flush ();
/* clear out pending port deletion list. we know this is safe because
* the auto connect thread in Session is already dead when this is
* done. It doesn't use shared_ptr<Port> anyway.
*/
_port_deletions_pending.reset ();
_port_remove_in_progress = false;
}
@ -300,6 +318,13 @@ PortManager::port_registration_failure (const std::string& portname)
throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
}
struct PortDeleter
{
void operator() (Port* p) {
AudioEngine::instance()->add_pending_port_deletion (p);
}
};
boost::shared_ptr<Port>
PortManager::register_port (DataType dtype, const string& portname, bool input, bool async, PortFlags flags)
{
@ -313,16 +338,19 @@ PortManager::register_port (DataType dtype, const string& portname, bool input,
if (dtype == DataType::AUDIO) {
DEBUG_TRACE (DEBUG::Ports, string_compose ("registering AUDIO port %1, input %2\n",
portname, input));
newport.reset (new AudioPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)));
newport.reset (new AudioPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
PortDeleter());
} else if (dtype == DataType::MIDI) {
if (async) {
DEBUG_TRACE (DEBUG::Ports, string_compose ("registering ASYNC MIDI port %1, input %2\n",
portname, input));
newport.reset (new AsyncMIDIPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)));
newport.reset (new AsyncMIDIPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
PortDeleter());
} else {
DEBUG_TRACE (DEBUG::Ports, string_compose ("registering MIDI port %1, input %2\n",
portname, input));
newport.reset (new MidiPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)));
newport.reset (new MidiPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
PortDeleter());
}
} else {
throw PortRegistrationFailure("unable to create port (unknown type)");

View File

@ -6896,6 +6896,12 @@ Session::auto_connect_route (boost::shared_ptr<Route> route, bool connect_inputs
input_start, output_start,
input_offset, output_offset));
auto_connect_thread_wakeup ();
}
void
Session::auto_connect_thread_wakeup ()
{
if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) {
pthread_cond_signal (&_auto_connect_cond);
pthread_mutex_unlock (&_auto_connect_mutex);
@ -6906,10 +6912,7 @@ void
Session::queue_latency_recompute ()
{
g_atomic_int_inc (&_latency_recompute_pending);
if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) {
pthread_cond_signal (&_auto_connect_cond);
pthread_mutex_unlock (&_auto_connect_mutex);
}
auto_connect_thread_wakeup ();
}
void
@ -7031,10 +7034,7 @@ Session::auto_connect_thread_terminate ()
}
}
if (pthread_mutex_lock (&_auto_connect_mutex) == 0) {
pthread_cond_signal (&_auto_connect_cond);
pthread_mutex_unlock (&_auto_connect_mutex);
}
auto_connect_thread_wakeup ();
void *status;
pthread_join (_auto_connect_thread, &status);
@ -7090,6 +7090,16 @@ Session::auto_connect_thread_run ()
}
}
std::cerr << "Autoconnect thread checking port deletions ...\n";
RingBuffer<Port*>& ports (AudioEngine::instance()->port_deletions_pending());
Port* p;
while (ports.read (&p, 1) == 1) {
std::cerr << "autoconnect deletes " << p->name() << std::endl;
delete p;
}
pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex);
}
pthread_mutex_unlock (&_auto_connect_mutex);