Fix race condition when closing a session

~Session calls AudioEngine::remove_session(), which fades out,
then unsets the session, and then in libs/ardour/audioengine.cc:460
```
session_removed.signal(); // wakes up thread that initiated session removal
```

Session d'tor continues, and calls Port::PortDrop(), which
unregisters all ports one at a time.

Concurrently, AudioEngine continues, and calls PortManager::silence_outputs
which gets all ports first (!), and then iterates over them.

There is a race condition which can lead to
DummyAudioBackend::get_buffer: Assertion `valid_port (port)' failed
This commit is contained in:
Robin Gareus 2023-04-24 16:54:50 +02:00
parent 40738b2bee
commit 95de61f74e
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
3 changed files with 14 additions and 8 deletions

View File

@ -270,6 +270,7 @@ class LIBARDOUR_API AudioEngine : public PortManager, public SessionHandlePtr
Glib::Threads::Cond session_removed;
bool session_remove_pending;
sampleoffset_t session_removal_countdown;
bool session_deleted;
gain_t session_removal_gain;
gain_t session_removal_gain_step;
bool _running;

View File

@ -84,6 +84,7 @@ static std::atomic<int> audioengine_thread_cnt (1);
AudioEngine::AudioEngine ()
: session_remove_pending (false)
, session_removal_countdown (-1)
, session_deleted (false)
, _running (false)
, _freewheeling (false)
, monitor_check_interval (INT32_MAX)
@ -261,12 +262,12 @@ AudioEngine::process_callback (pframes_t nframes)
if (_session) {
Xrun();
}
/* really only JACK requires this
* (other backends clear the output buffers
* before the process_callback. it may even be
* jack/alsa only). but better safe than sorry.
/* only JACK requires this (other backends clear the
* output buffers before the process_callback.
*/
PortManager::silence_outputs (nframes);
if (!session_deleted) {
PortManager::silence_outputs (nframes);
}
return 0;
}
@ -454,7 +455,9 @@ AudioEngine::process_callback (pframes_t nframes)
} else {
/* fade out done */
_session = 0;
PortManager::silence_outputs (nframes);
session_deleted = true;
SessionHandlePtr::set_session (0);
session_removal_countdown = -1; // reset to "not in progress"
session_remove_pending = false;
session_removed.signal(); // wakes up thread that initiated session removal
@ -475,7 +478,7 @@ AudioEngine::process_callback (pframes_t nframes)
if (_session == 0) {
if (!_freewheeling) {
if (!_freewheeling && !session_deleted) {
PortManager::silence_outputs (nframes);
}
@ -799,6 +802,7 @@ AudioEngine::set_session (Session *s)
SessionHandlePtr::set_session (s);
if (_session) {
session_deleted = false;
_init_countdown = std::max (4, (int)(_backend->sample_rate () / _backend->buffer_size ()) / 8);
_pending_playback_latency_callback.store (0);
_pending_capture_latency_callback.store (0);
@ -820,6 +824,7 @@ AudioEngine::remove_session ()
}
} else {
session_deleted = true;
SessionHandlePtr::set_session (0);
}

View File

@ -155,7 +155,7 @@ Port::drop ()
if (_port_handle) {
DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
port_engine.unregister_port (_port_handle);
_port_handle.reset ();;
_port_handle.reset ();
}
}