XMLNode& get_state ();
int set_state(const XMLNode&, int version);
+ PBD::Signal0<void> SelfDestruct;
+ void set_remove_on_disconnect (bool b) { _remove_on_disconnect = b; }
+ bool remove_on_disconnect () const { return _remove_on_disconnect; }
+
uint32_t pans_required() const { return _configured_input.n_audio(); }
void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool);
/* disallow copy construction */
Send (const Send&);
void panshell_changed ();
+ void snd_output_changed (IOChange, void*);
int set_state_2X (XMLNode const &, int);
framecnt_t _delay_in;
framecnt_t _delay_out;
+ bool _remove_on_disconnect;
};
} // namespace ARDOUR
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
+ boost::shared_ptr<Send> send;
+ if ((send = boost::dynamic_pointer_cast<Send> (processor))) {
+ send->SelfDestruct.connect_same_thread (*this,
+ boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
+ }
+
return 0;
}
+void
+Route::processor_selfdestruct (boost::weak_ptr<Processor> wp)
+{
+ /* We cannot destruct the processor here (usually RT-thread
+ * with various locks held - in case of sends also io_locks).
+ * Queue for deletion in low-priority thread.
+ */
+ Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+ selfdestruct_sequence.push_back (wp);
+}
+
bool
Route::add_processor_from_xml_2X (const XMLNode& node, int version)
{
} else if (prop->value() == "send") {
processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true));
+ boost::shared_ptr<Send> send = boost::dynamic_pointer_cast<Send> (processor);
+ send->SelfDestruct.connect_same_thread (*this,
+ boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
} else {
error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
g_atomic_int_set (&_pending_signals, emissions);
return true;
}
- return false;
+ return (!selfdestruct_sequence.empty ());
}
void
Route::emit_pending_signals ()
{
-
int sig = g_atomic_int_and (&_pending_signals, 0);
if (sig & EmitMeterChanged) {
_meter->emit_configuration_changed();
if (sig & EmitRtProcessorChange) {
processors_changed (RouteProcessorChange (RouteProcessorChange::RealTimeChange)); /* EMIT SIGNAL */
}
+
+ /* this would be a job for the butler.
+ * Conceptually we should not take processe/processor locks here.
+ * OTOH its more efficient (less overhead for summoning the butler and
+ * telling her what do do) and signal emission is called
+ * directly after the process callback, which decreases the chance
+ * of x-runs when taking the locks.
+ */
+ while (!selfdestruct_sequence.empty ()) {
+ Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+ if (selfdestruct_sequence.empty ()) { break; } // re-check with lock
+ boost::shared_ptr<Processor> proc = selfdestruct_sequence.back ().lock ();
+ selfdestruct_sequence.pop_back ();
+ lx.release ();
+ if (proc) {
+ remove_processor (proc);
+ }
+ }
}
void
, _metering (false)
, _delay_in (0)
, _delay_out (0)
+ , _remove_on_disconnect (false)
{
if (_role == Listen) {
/* we don't need to do this but it keeps things looking clean
if (panner_shell()) {
panner_shell()->Changed.connect_same_thread (*this, boost::bind (&Send::panshell_changed, this));
}
+ if (_output) {
+ _output->changed.connect_same_thread (*this, boost::bind (&Send::snd_output_changed, this, _1, _2));
+ }
}
Send::~Send ()
node.add_property ("bitslot", buf);
}
+ node.add_property("selfdestruct", _remove_on_disconnect ? "yes" : "no");
+
node.add_child_nocopy (_amp->state (full));
return node;
}
}
+ if ((prop = node.property (X_("selfdestruct"))) != 0) {
+ _remove_on_disconnect = string_is_affirmative (prop->value());
+ }
+
XMLNodeList nlist = node.children();
for (XMLNodeIterator i = nlist.begin(); i != nlist.end(); ++i) {
if ((*i)->name() == X_("Processor")) {
{
return _amp->value_as_string (ac);
}
+
+void
+Send::snd_output_changed (IOChange change, void* /*src*/)
+{
+ if (change.type & IOChange::ConnectionsChanged) {
+ if (!_output->connected() && _remove_on_disconnect) {
+ _remove_on_disconnect = false;
+ SelfDestruct (); /* EMIT SIGNAL */
+ }
+ }
+}