Prevent deadlock when disconnecting

The backend holds `_port_callback_mutex` while disconnecting ports.
In some cases disconnecting a port can drop the last reference
resulting in a port-deletion from the connection handler.
This in turn will eventually aquire the `_port_callback_mutex`
and deadlock.

This is now circumvented by using atomic operations instead of
taking a lock to set the `_port_change_flag`.

The flag is also used to trigger a latency update in some cases,
atomic is preferable to taking a lock to set this flag.

--

Full bt: https://paste.debian.net/1184056/
Short:

#1  in pthread_mutex_lock ()
#2  in ARDOUR::PortEngineSharedImpl::port_connect_add_remove_callback()
#3  in ARDOUR::BackendPort::~BackendPort()
#4  in ARDOUR::DummyPort::~DummyPort()
#6  in ARDOUR::DummyAudioPort::~DummyAudioPort()
#7  in boost::checked_delete<ARDOUR::BackendPort>(ARDOUR::BackendPort*)
#12 in boost::shared_ptr<ARDOUR::ProtoPort>::reset()
#13 in ARDOUR::Port::drop()
#14 in ARDOUR::Port::~Port()
#15 in ARDOUR::AudioPort::~AudioPort()
#17 in ARDOUR::AudioEngine::add_pending_port_deletion(ARDOUR::Port*)
#20 in boost::detail::sp_counted_base::release()
#37 in ARDOUR::PortManager::connect_callback() at libs/ardour/port_manager.cc:788
#38 in ARDOUR::DummyAudioBackend::main_process_thread() at libs/backends/dummy/dummy_audiobackend.cc:1018
This commit is contained in:
Robin Gareus 2021-02-04 21:04:40 +01:00
parent 30da8c00b5
commit 634d325e5d
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
7 changed files with 21 additions and 29 deletions

View file

@ -640,7 +640,7 @@ CoreAudioBackend::_start (bool for_latency_measurement)
_preinit = true;
_run = true;
_port_change_flag = false;
g_atomic_int_set (&_port_change_flag, 0);
if (_midi_driver_option == _("CoreMidi")) {
_midiio->set_enabled(true);
@ -698,7 +698,7 @@ CoreAudioBackend::_start (bool for_latency_measurement)
engine.reconnect_ports ();
// force an initial registration_callback() & latency re-compute
_port_change_flag = true;
g_atomic_int_set (&_port_change_flag, 1);
pre_process ();
_dsp_load_calc.reset ();
@ -988,7 +988,7 @@ CoreAudioBackend::coremidi_rediscover()
#ifndef NDEBUG
printf("unregister MIDI Output: %s\n", (*it)->name().c_str());
#endif
_port_change_flag = true;
g_atomic_int_set (&_port_change_flag, 1);
unregister_port((*it));
it = _system_midi_out.erase(it);
}
@ -1008,7 +1008,7 @@ CoreAudioBackend::coremidi_rediscover()
#ifndef NDEBUG
printf("unregister MIDI Input: %s\n", (*it)->name().c_str());
#endif
_port_change_flag = true;
g_atomic_int_set (&_port_change_flag, 1);
unregister_port((*it));
it = _system_midi_in.erase(it);
}
@ -1034,7 +1034,7 @@ CoreAudioBackend::coremidi_rediscover()
BackendPortPtr pp = boost::dynamic_pointer_cast<BackendPort>(p);
pp->set_hw_port_name(_midiio->port_name(i, true));
_system_midi_in.push_back(pp);
_port_change_flag = true;
g_atomic_int_set (&_port_change_flag, 1);
}
for (size_t i = 0; i < _midiio->n_midi_outputs(); ++i) {
@ -1057,7 +1057,7 @@ CoreAudioBackend::coremidi_rediscover()
BackendPortPtr pp = boost::dynamic_pointer_cast<BackendPort>(p);
pp->set_hw_port_name(_midiio->port_name(i, false));
_system_midi_out.push_back(pp);
_port_change_flag = true;
g_atomic_int_set (&_port_change_flag, 1);
}
@ -1229,9 +1229,8 @@ CoreAudioBackend::pre_process ()
bool connections_changed = false;
bool ports_changed = false;
if (!pthread_mutex_trylock (&_port_callback_mutex)) {
if (_port_change_flag) {
if (g_atomic_int_compare_and_exchange (&_port_change_flag, 1, 0)) {
ports_changed = true;
_port_change_flag = false;
}
if (!_port_connection_queue.empty ()) {
connections_changed = true;