From a67a4754809285c0d3bfcf3298deead79f69f3fb Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 11 Jun 2021 10:41:41 -0600 Subject: [PATCH] dramtically improve performance of ::get_port_by_name() for JACK backend --- libs/backends/jack/jack_audiobackend.h | 7 +- libs/backends/jack/jack_portengine.cc | 88 ++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 14 deletions(-) diff --git a/libs/backends/jack/jack_audiobackend.h b/libs/backends/jack/jack_audiobackend.h index c868137f0a..59febab36e 100644 --- a/libs/backends/jack/jack_audiobackend.h +++ b/libs/backends/jack/jack_audiobackend.h @@ -303,7 +303,12 @@ class JACKAudioBackend : public AudioBackend { static void _registration_callback (jack_port_id_t, int, void *); static void _connect_callback (jack_port_id_t, jack_port_id_t, int, void *); - typedef std::map > JackPorts; + /* used to manage _jack_ports, specifically for ports belonging to the + JACK backend or other clients. + */ + void jack_registration_callback (jack_port_id_t, int); + + typedef std::map > JackPorts; mutable SerializedRCUManager _jack_ports; /* can be modified in ::get_port_by_name () */ void connect_callback (jack_port_id_t, jack_port_id_t, int); diff --git a/libs/backends/jack/jack_portengine.cc b/libs/backends/jack/jack_portengine.cc index fc084f68ca..aa3c46ed0c 100644 --- a/libs/backends/jack/jack_portengine.cc +++ b/libs/backends/jack/jack_portengine.cc @@ -175,28 +175,32 @@ JACKAudioBackend::set_port_property (PortHandle port, const std::string& key, co PortEngine::PortPtr JACKAudioBackend::get_port_by_name (const std::string& name) const { + { + boost::shared_ptr ports = _jack_ports.reader (); + JackPorts::iterator p = ports->find (name); + + if (p != ports->end()) { + return p->second; + } + } + + /* Port not known to us yet, so look it up via JACK (slow) */ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, PortEngine::PortPtr()); jack_port_t * jack_port = jack_port_by_name (_priv_jack, name.c_str()); if (!jack_port) { + /* No such port ... return nothering */ return PortEngine::PortPtr(); } - boost::shared_ptr ports = _jack_ports.reader (); - - JackPorts::const_iterator i = ports->find (jack_port); - - if (i != ports->end()) { - return i->second; - } - boost::shared_ptr jp; { RCUWriter writer (_jack_ports); boost::shared_ptr ports = writer.get_copy(); jp.reset (new JackPort (jack_port)); - ports->insert (std::make_pair (jack_port, jp)); + ports->insert (std::make_pair (name, jp)); } _jack_ports.flush (); @@ -205,13 +209,71 @@ JACKAudioBackend::get_port_by_name (const std::string& name) const } void -JACKAudioBackend::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* arg) +JACKAudioBackend::_registration_callback (jack_port_id_t id, int reg, void* arg) { + /* we don't use a virtual method for the registration callback, because + JACK is the only backend that delivers the arguments shown above. So + call our own JACK-centric registration callback, then the generic + one. + */ + static_cast (arg)->jack_registration_callback (id, reg); static_cast (arg)->manager.registration_callback (); static_cast (arg)->engine.latency_callback (false); static_cast (arg)->engine.latency_callback (true); } +void +JACKAudioBackend::jack_registration_callback (jack_port_id_t id, int reg) +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + jack_port_t* jack_port = jack_port_by_id (_priv_jack, id); + + if (!jack_port) { + return; + } + + /* We only need to care about ports that we do not register/unregister + * ourselves. Our own ports will added/removed from _jack_ports at the + * appropriate time. + * + * But for the input meters, we'll be looking up ports not created by + * us, and they may also go away at arbitrary times too. We want to + * make sure we can look up these ports by name only (in _jack_ports) + * because jack_port_by_name() is unacceptably slow for RT contexts + * (like run_input_meters()). So we catch these ports at registration + * time, and put a suitable entry in _jack_ports. + * + * It isn't critical that we keep _jack_ports current if any of these + * ports goes away, but since we get told about that here, we do that + * just to keep things clean. This will happen if someone disconnects a + * USB MIDI device, for example. + */ + + if (!jack_port_is_mine (_priv_jack, jack_port)) { + + const char* name = jack_port_name (jack_port); + + boost::shared_ptr ports = _jack_ports.write_copy(); + + if (!reg) { + if (ports->erase (name)) { + _jack_ports.update (ports); + } + } else { + if (ports->find (name) != ports->end()) { + /* hmmm, we already have this port */ + std::cout << "re-registration of JACK port named " << name << std::endl; + ports->erase (name); + } + + boost::shared_ptr jp (new JackPort (jack_port)); + ports->insert (std::make_pair (name, jp)); + + _jack_ports.update (ports); + } + } +} + int JACKAudioBackend::_graph_order_callback (void *arg) { @@ -523,7 +585,7 @@ JACKAudioBackend::register_port (const std::string& shortname, ARDOUR::DataType jp.reset (new JackPort (jack_port)); - ports->insert (std::make_pair (jack_port, jp)); + ports->insert (std::make_pair (jack_port_name (jack_port), jp)); } _jack_ports.flush(); @@ -536,12 +598,12 @@ JACKAudioBackend::unregister_port (PortHandle port) { GET_PRIVATE_JACK_POINTER (_priv_jack); boost::shared_ptr jp = boost::dynamic_pointer_cast(port); + const std::string name = jack_port_name (jp->jack_ptr); { RCUWriter writer (_jack_ports); boost::shared_ptr ports = writer.get_copy(); - - ports->erase (jp->jack_ptr); + ports->erase (name); } _jack_ports.flush ();