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.
PBD::Signal0<void> BecameSilent;
void reset_silence_countdown ();
+ void add_pending_port_deletion (Port*);
+
private:
AudioEngine ();
#include <boost/shared_ptr.hpp>
#include "pbd/rcu.h"
+#include "pbd/ringbuffer.h"
#include "ardour/chan_count.h"
#include "ardour/midiport_manager.h"
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 */
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);
VCAManager& vca_manager() { return *_vca_manager; }
+ void auto_connect_thread_wakeup ();
+
protected:
friend class AudioEngine;
void set_block_size (pframes_t nframes);
{
_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;
+ }
+}
+
MidiPort::~MidiPort()
{
if (_shadow_port) {
- _shadow_port->disconnect_all ();
+ AudioEngine::instance()->unregister_port (_shadow_port);
+ _shadow_port.reset ();
}
delete _buffer;
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 ()
{
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;
}
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)
{
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)");
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);
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
}
}
- 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);
}
}
+ 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);