From b5e479dfc31476b4159abd51158863fe62537496 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jul 2020 00:15:26 +0200 Subject: [PATCH] Handle JACK latency callbacks in sync with process thread #8304 This is only relevant with JACK, where different implementations use threads for the latency callback. With jack 2, jack_port_register() blocks and the jack_latency_callback arrives in a different thread: https://pastebin.com/mitGBwpq with jack 1 the callback arrives in sync In either case this usually happens while _adding_routes_in_progress == true and Ardour holds the process-lock, because jack2 can process in parallel with reconfiguring latency See also 1983f56592dfea5f7498 --- libs/ardour/ardour/audioengine.h | 2 ++ libs/ardour/audioengine.cc | 39 +++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 3d13240fd6..f99d9bafd1 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -309,6 +309,8 @@ class LIBARDOUR_API AudioEngine : public PortManager, public SessionHandlePtr gint _stop_hw_devicelist_processing; uint32_t _start_cnt; uint32_t _init_countdown; + volatile gint _pending_playback_latency_callback; + volatile gint _pending_capture_latency_callback; void start_hw_event_processing(); void stop_hw_event_processing(); diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 7db7bc5d21..e7073d63ae 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -106,6 +106,8 @@ AudioEngine::AudioEngine () , _stop_hw_devicelist_processing(0) , _start_cnt (0) , _init_countdown (0) + , _pending_playback_latency_callback (0) + , _pending_capture_latency_callback (0) #ifdef SILENCE_AFTER_SECONDS , _silence_countdown (0) , _silence_hit_cnt (0) @@ -282,6 +284,20 @@ AudioEngine::process_callback (pframes_t nframes) thread_init_callback (NULL); } + /* This is for JACK, where the latency callback arrives in sync with + * port registration (usually while ardour holds the process-lock + * or with _adding_routes_in_progress or _route_deletion_in_progress set, + * potentially while processing in parallel. + */ + if (_session) { + if (g_atomic_int_compare_and_exchange (&_pending_playback_latency_callback, 1, 0)) { + _session->update_latency (true); + } + if (g_atomic_int_compare_and_exchange (&_pending_capture_latency_callback, 1, 0)) { + _session->update_latency (false); + } + } + if (_session && _init_countdown > 0) { --_init_countdown; /* Warm up caches */ @@ -736,6 +752,8 @@ AudioEngine::set_session (Session *s) if (_session) { _init_countdown = std::max (4, (int)(_backend->sample_rate () / _backend->buffer_size ()) / 8); + g_atomic_int_set (&_pending_playback_latency_callback, 0); + g_atomic_int_set (&_pending_capture_latency_callback, 0); } } @@ -1399,7 +1417,26 @@ AudioEngine::latency_callback (bool for_playback) { DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose (X_("latency callback playback ? %1\n"), for_playback)); if (_session) { - _session->update_latency (for_playback); + if (in_process_thread ()) { + /* internal backends emit the latency callback in the rt-callback, + * async to connect/disconnect or port creation/deletion. + * All is fine. + * + * However jack 1/2 emit the callback in sync with creating the port + * (or while handling the connection change). + * e.g. JACK2 jack_port_register() blocks and the jack_latency_callback + * from a different thread: https://pastebin.com/mitGBwpq + * but at this point in time Ardour still holds the process callback + * because JACK2 can process in parallel to latency callbacks. + * + * see also Session::update_latency() and git-ref 1983f56592dfea5f7498 + */ + _session->update_latency (for_playback); + } else if (for_playback) { + g_atomic_int_set (&_pending_playback_latency_callback, 1); + } else { + g_atomic_int_set (&_pending_capture_latency_callback, 1); + } } }