13
0

Fix deadlock/race introduced in aaf301321

Port::_connections_lock must not be locked when calling
port_engine.connect().
This commit is contained in:
Robin Gareus 2023-05-27 14:18:31 +02:00
parent abd27b765e
commit 10b2380b14
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04

View File

@ -714,52 +714,62 @@ Port::has_ext_connection () const
int int
Port::reconnect () Port::reconnect ()
{ {
/* caller must hold process lock; intended to be used only after reestablish() */
int count = 0;
std::string const bid (AudioEngine::instance()->backend_id (receives_input ())); std::string const bid (AudioEngine::instance()->backend_id (receives_input ()));
Glib::Threads::RWLock::WriterLock lm (_connections_lock); std::vector <std::string> c_int, c_ext, f_int, f_ext;
Glib::Threads::RWLock::ReaderLock lm (_connections_lock);
if (_ext_connections.find (bid) != _ext_connections.end ()) { if (_ext_connections.find (bid) != _ext_connections.end ()) {
if (_int_connections.empty () && _ext_connections[bid].empty ()) { if (_int_connections.empty () && _ext_connections[bid].empty ()) {
return 0; /* OK */ return 0; /* OK */
} }
count = _int_connections.size() + _ext_connections[bid].size(); c_int.insert (c_int.end(), _int_connections.begin(), _int_connections.end());
c_ext.insert (c_ext.end(), _ext_connections.at(bid).begin(), _ext_connections.at(bid).end());
} else { } else {
if (_int_connections.empty ()) { if (_int_connections.empty ()) {
return 0; /* OK */ return 0; /* OK */
} }
count = _int_connections.size(); c_int.insert (c_int.end(), _int_connections.begin(), _int_connections.end());
} }
DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() Connect %1 to %2 destinations\n",name(), count)); /* Must hold the lock while calling port_engine.connect. It could lead to deadlock:
*
* XXBackend::main_process_thread -> PortManager::connect_callback
* -> Port::port_connected_or_disconnected -> Port::insert_connection -> take WriterLock
*/
lm.release ();
count = 0; DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() Connect %1 to %2 destinations\n",name(), c_int.size () + c_ext.size ()));
ConnectionSet::iterator i = _int_connections.begin(); int count = 0;
while (i != _int_connections.end()) {
ConnectionSet::iterator current = i++; for (auto const& c : c_int) {
if (connect_internal (*current)) { if (connect_internal (c)) {
DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() failed to connect %1 to %2\n", name(), (*current))); DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() failed to connect %1 to %2\n", name(), (c)));
_int_connections.erase (current); f_int.push_back (c);
} else { } else {
++count; ++count;
} }
} }
if (_ext_connections.find (bid) != _ext_connections.end ()) { for (auto const& c : c_ext) {
i = _ext_connections[bid].begin(); if (connect_internal (c)) {
while (i != _ext_connections[bid].end()) { DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() failed to connect %1 to %2\n", name(), (c)));
ConnectionSet::iterator current = i++; f_ext.push_back (c);
if (connect_internal (*current)) {
DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() failed to connect %1 to %2\n", name(), (*current)));
_ext_connections[bid].erase (current);
} else { } else {
++count; ++count;
} }
} }
lm.acquire ();
for (auto const& c : f_int) {
_int_connections.erase (c);
}
for (auto const& c : f_ext) {
_ext_connections[bid].erase (c);
} }
return count == 0 ? -1 : 0; return count == 0 ? -1 : 0;