fix race condition when dropping Ports
authorPaul Davis <paul@linuxaudiosystems.com>
Mon, 8 Aug 2016 13:26:50 +0000 (09:26 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Mon, 8 Aug 2016 13:27:21 +0000 (09:27 -0400)
Jack2 calls back from a notification thread and the callback (PortManager::connect_callback())
could end up holding the final reference on 1 or more ports. The ports would then be
unregistered as we leave the callback scope, which is illegal (no server calls from
a notification thread)

libs/ardour/io.cc

index f7e2b95eb381bd2bed3328719b8b1ffa0afb30bd..f535467cbe664874408bf32454546d030124b54e 100644 (file)
@@ -405,6 +405,7 @@ IO::ensure_ports_locked (ChanCount count, bool clear, bool& changed)
 #endif
 
        boost::shared_ptr<Port> port;
+       vector<boost::shared_ptr<Port> > deleted_ports;
 
        changed    = false;
 
@@ -418,11 +419,30 @@ IO::ensure_ports_locked (ChanCount count, bool clear, bool& changed)
 
                        assert(port);
                        _ports.remove(port);
+
+                       /* hold a reference to the port so that we can ensure
+                        * that this thread, and not a JACK notification thread,
+                        * holds the final reference.
+                        */
+
+                       deleted_ports.push_back (port);
                        _session.engine().unregister_port (port);
 
                        changed = true;
                }
 
+               /* this will drop the final reference to the deleted ports,
+                * which will in turn call their destructors, which will in
+                * turn call the backend to unregister them.
+                *
+                * There will no connect/disconnect or register/unregister
+                * callbacks from the backend until we get here, because
+                * they are driven by the Port destructor. The destructor
+                * will not execute until we drop the final reference,
+                * which all happens right .... here.
+                */
+               deleted_ports.clear ();
+
                /* create any necessary new ports */
                while (n_ports().get(*t) < n) {