redesign cross-thread registration/signalling system
authorPaul Davis <paul@linuxaudiosystems.com>
Mon, 28 Dec 2015 15:14:17 +0000 (10:14 -0500)
committerPaul Davis <paul@linuxaudiosystems.com>
Mon, 28 Dec 2015 15:14:17 +0000 (10:14 -0500)
This new design will work even when threads that need to receive
messages from RT threads are created *after* the RT threads. The
existing design would fail because the RT thread(s) would never
be known the later created threads, and so signals emitted by the
RT thread and causing call_slot() in the receiver would end up
being enqueued using a lock-protected list. The new design ensures
that communication always uses a lock-free FIFO instead

25 files changed:
gtk2_ardour/strip_silence_dialog.cc
libs/ardour/ardour/control_protocol_manager.h
libs/ardour/ardour/midi_ui.h
libs/ardour/audioengine.cc
libs/ardour/control_protocol_manager.cc
libs/ardour/globals.cc
libs/ardour/midi_ui.cc
libs/gtkmm2ext/gtk_ui.cc
libs/pbd/event_loop.cc
libs/pbd/pbd/abstract_ui.cc
libs/pbd/pbd/abstract_ui.h
libs/pbd/pbd/event_loop.h
libs/pbd/pbd/pthread_utils.h
libs/pbd/pthread_utils.cc
libs/surfaces/control_protocol/control_protocol/control_protocol.h
libs/surfaces/faderport/faderport.cc
libs/surfaces/faderport/faderport.h
libs/surfaces/faderport/faderport_interface.cc
libs/surfaces/mackie/interface.cc
libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/mackie/mackie_control_protocol.h
libs/surfaces/osc/interface.cc
libs/surfaces/osc/osc.cc
libs/surfaces/osc/osc.h
libs/surfaces/wiimote/wiimote.cc

index 71d25d648e8807e8e5be4e4be1e21dc4f4f5340c..cd41950924d37f28dfe952b6d71739de30aceffb 100644 (file)
@@ -237,7 +237,10 @@ StripSilenceDialog::_detection_thread_work (void* arg)
 void *
 StripSilenceDialog::detection_thread_work ()
 {
-       ARDOUR_UI::instance()->register_thread ("gui", pthread_self(), "silence", 32);
+       /* Do not register with all UIs, but do register with the GUI,
+          because we will need to queue some GUI (only) requests
+       */
+       ARDOUR_UI::instance()->register_thread (pthread_self(), "silence", 32);
 
        /* Hold this lock when we are doing work */
        _lock.lock ();
index 0c14d2a049c3ca54860b9b8a213aef050a587a90..dbbb0c38913003c39e3281bbb8e879e68e6f9d8e 100644 (file)
@@ -65,6 +65,7 @@ class LIBARDOUR_API ControlProtocolManager : public PBD::Stateful, public ARDOUR
        void load_mandatory_protocols ();
        void midi_connectivity_established ();
        void drop_protocols ();
+       void register_request_buffer_factories ();
 
        int activate (ControlProtocolInfo&);
         int deactivate (ControlProtocolInfo&);
index 7f57f22e2487672c6dee703483db7c8ffaaa6c13..2f0f7d3a45e47b0372d4e7646f83edca97a500df 100644 (file)
@@ -51,6 +51,7 @@ class LIBARDOUR_API MidiControlUI : public AbstractUI<MidiUIRequest>
        ~MidiControlUI ();
 
        static MidiControlUI* instance() { return _instance; }
+       static void* request_factory (uint32_t num_requests);
 
        void change_midi_ports ();
 
index 3452d1d14c313d16443b70d52055b3cba400b1ea..9c8407e8d710ec86299a5fc013b0e1777a36ea75 100644 (file)
@@ -1226,8 +1226,7 @@ AudioEngine::thread_init_callback (void* arg)
 
        SessionEvent::create_per_thread_pool (X_("AudioEngine"), 512);
 
-       PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("AudioEngine"), 4096);
-       PBD::notify_gui_about_thread_creation ("midiUI", pthread_self(), X_("AudioEngine"), 128);
+       PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("AudioEngine"), 4096);
 
        AsyncMIDIPort::set_process_thread (pthread_self());
 
index 4e2cb7d9f76aa6aca6073d462553b4c28268d98e..6583869fed99e1a791ccd75aa3679640adfc0896 100644 (file)
@@ -22,6 +22,7 @@
 #include <glibmm/fileutils.h>
 
 #include "pbd/compose.h"
+#include "pbd/event_loop.h"
 #include "pbd/file_utils.h"
 #include "pbd/error.h"
 
@@ -487,3 +488,15 @@ ControlProtocolManager::midi_connectivity_established ()
                (*p)->midi_connectivity_established ();
        }
 }
+
+void
+ControlProtocolManager::register_request_buffer_factories ()
+{
+       Glib::Threads::Mutex::Lock lm (protocols_lock);
+
+       for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+               if ((*i)->descriptor->request_buffer_factory) {
+                       EventLoop::register_request_buffer_factory ((*i)->descriptor->name, (*i)->descriptor->request_buffer_factory);
+               }
+       }
+}
index b2ad67d0538ccce16a2c25464274cae3e7df7057..9f4a1399b3d4ed30d24aedda2bc791843deb62f9 100644 (file)
@@ -97,6 +97,7 @@
 #include "ardour/event_type_map.h"
 #include "ardour/filesystem_paths.h"
 #include "ardour/midi_region.h"
+#include "ardour/midi_ui.h"
 #include "ardour/midiport_manager.h"
 #include "ardour/mix.h"
 #include "ardour/operations.h"
@@ -513,6 +514,22 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
 #endif
        (void) EventTypeMap::instance();
 
+       ControlProtocolManager::instance().discover_control_protocols ();
+
+       /* for each control protocol, check for a request buffer factory method
+          and if it exists, store it in the EventLoop list of such
+          methods. This allows the relevant threads to register themselves
+          with EventLoops so that signal emission can be RT-safe.
+       */
+
+       ControlProtocolManager::instance().register_request_buffer_factories ();
+       /* it would be nice if this could auto-register itself in the
+          constructor, since MidiControlUI is a singleton, but it can't be
+          created until after the engine is running. Therefore we have to
+          explicitly register it here.
+       */
+       EventLoop::register_request_buffer_factory (X_("midiUI"), MidiControlUI::request_factory);
+
         ProcessThread::init ();
        /* the + 4 is a bit of a handwave. i don't actually know
           how many more per-thread buffer sets we need above
@@ -553,8 +570,6 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
 void
 ARDOUR::init_post_engine ()
 {
-       ControlProtocolManager::instance().discover_control_protocols ();
-
        XMLNode* node;
        if ((node = Config->control_protocol_state()) != 0) {
                ControlProtocolManager::instance().set_state (*node, Stateful::loading_state_version);
index 96304fa05174b588d30ef6b51ae68fed2dcb7260..1d2fe7c7e1b4a2b2b6fcdb02357c7f54a9231f2e 100644 (file)
@@ -60,6 +60,17 @@ MidiControlUI::~MidiControlUI ()
        _instance = 0;
 }
 
+void*
+MidiControlUI::request_factory (uint32_t num_requests)
+{
+       /* AbstractUI<T>::request_buffer_factory() is a template method only
+          instantiated in this source module. To provide something visible for
+          use when registering the factory, we have this static method that is
+          template-free.
+       */
+       return request_buffer_factory (num_requests);
+}
+
 void
 MidiControlUI::do_request (MidiUIRequest* req)
 {
@@ -131,7 +142,7 @@ MidiControlUI::thread_init ()
 
        pthread_set_name (X_("midiUI"));
 
-       PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("midiUI"), 2048);
+       PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("midiUI"), 2048);
        SessionEvent::create_per_thread_pool (X_("midiUI"), 128);
 
        memset (&rtparam, 0, sizeof (rtparam));
index ca4d6681dce46f567143396677576f586ff2c9d4..538bf8d63f5b060bdf78f9c23573673d0424cfc7 100644 (file)
@@ -98,6 +98,10 @@ UI::UI (string namestr, int *argc, char ***argv)
 
        set_event_loop_for_thread (this);
 
+       /* we will be receiving requests */
+
+       EventLoop::register_request_buffer_factory ("gui", request_buffer_factory);
+
        /* attach our request source to the default main context */
 
        attach_request_source ();
index 95b43d9038e2deb78cf965a727d57b44474f6f50..671e26bedc7bb4862f05a941e4a5e077f40126b7 100644 (file)
 
 */
 
+#include "pbd/compose.h"
 #include "pbd/event_loop.h"
+#include "pbd/error.h"
 #include "pbd/stacktrace.h"
 
+#include "i18n.h"
+
 using namespace PBD;
 using namespace std;
 
@@ -27,13 +31,18 @@ static void do_not_delete_the_loop_pointer (void*) { }
 
 Glib::Threads::Private<EventLoop> EventLoop::thread_event_loop (do_not_delete_the_loop_pointer);
 
+Glib::Threads::RWLock EventLoop::thread_buffer_requests_lock;
+EventLoop::ThreadRequestBufferList EventLoop::thread_buffer_requests;
+EventLoop::RequestBufferSuppliers EventLoop::request_buffer_suppliers;
+
 EventLoop::EventLoop (string const& name)
        : _name (name)
 {
 }
 
 EventLoop*
-EventLoop::get_event_loop_for_thread() {
+EventLoop::get_event_loop_for_thread()
+{
        return thread_event_loop.get ();
 }
 
@@ -84,3 +93,98 @@ EventLoop::invalidate_request (void* data)
         return 0;
 }
 
+vector<EventLoop::ThreadBufferMapping>
+EventLoop::get_request_buffers_for_target_thread (const std::string& target_thread)
+{
+       vector<ThreadBufferMapping> ret;
+       Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
+
+       for (ThreadRequestBufferList::const_iterator x = thread_buffer_requests.begin();
+            x != thread_buffer_requests.end(); ++x) {
+
+               if (x->second.target_thread_name == target_thread) {
+                       ret.push_back (x->second);
+               }
+       }
+
+       return ret;
+}
+
+void
+EventLoop::register_request_buffer_factory (const string& target_thread_name,
+                                            void* (*factory)(uint32_t))
+{
+
+       RequestBufferSupplier trs;
+       trs.name = target_thread_name;
+       trs.factory = factory;
+
+       {
+               Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
+               request_buffer_suppliers.push_back (trs);
+       }
+}
+
+void
+EventLoop::pre_register (const string& emitting_thread_name, uint32_t num_requests)
+{
+       /* Threads that need to emit signals "towards" other threads, but with
+          RT safe behavior may be created before the receiving threads
+          exist. This makes it impossible for them to use the
+          ThreadCreatedWithRequestSize signal to notify receiving threads of
+          their existence.
+
+          This function creates a request buffer for them to use with
+          the (not yet) created threads, and stores it where the receiving
+          thread can find it later.
+        */
+
+       ThreadBufferMapping mapping;
+       Glib::Threads::RWLock::ReaderLock lm (thread_buffer_requests_lock);
+
+       for (RequestBufferSuppliers::iterator trs = request_buffer_suppliers.begin(); trs != request_buffer_suppliers.end(); ++trs) {
+
+               if (!trs->factory) {
+                       /* no factory - no request buffer required or expected */
+                       continue;
+               }
+
+               if (emitting_thread_name == trs->name) {
+                       /* no need to register an emitter with itself */
+                       continue;
+               }
+
+               mapping.emitting_thread = pthread_self();
+               mapping.target_thread_name = trs->name;
+
+               /* Allocate a suitably sized request buffer. This will set the
+                * thread-local variable that holds a pointer to this request
+                * buffer.
+                */
+               mapping.request_buffer = trs->factory (num_requests);
+
+               /* now store it where the receiving thread (trs->name) can find
+                  it if and when it is created. (Discovery happens in the
+                  AbstractUI constructor. Note that if
+               */
+
+               /* make a key composed of the emitter and receiver thread names */
+
+               string key = emitting_thread_name;
+               key += '/';
+               key +=  mapping.target_thread_name;
+
+               /* if the emitting thread was killed and recreated (with the
+                * same name), this will replace the entry in
+                * thread_buffer_requests. The old entry will be lazily deleted
+                * when the target thread finds the request buffer and realizes
+                * that it is dead.
+                *
+                * If the request buffer is replaced before the target thread
+                * ever finds the dead version, we will leak the old request
+                * buffer.
+                */
+
+               thread_buffer_requests[key] = mapping;
+       }
+}
index d3d3c2e8b10c82c758c53f905f6d1c243e32e6fb..1514455b42c86b9d8c1f2028f5333e0acedca784 100644 (file)
@@ -14,7 +14,6 @@
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
 */
 
 #include <unistd.h>
@@ -65,17 +64,28 @@ template <typename RequestObject>
 AbstractUI<RequestObject>::AbstractUI (const string& name)
        : BaseUI (name)
 {
-       void (AbstractUI<RequestObject>::*pmf)(string,pthread_t,string,uint32_t) = &AbstractUI<RequestObject>::register_thread;
+       void (AbstractUI<RequestObject>::*pmf)(pthread_t,string,uint32_t) = &AbstractUI<RequestObject>::register_thread;
 
        /* better to make this connect a handler that runs in the UI event loop but the syntax seems hard, and
           register_thread() is thread safe anyway.
        */
 
-       PBD::ThreadCreatedWithRequestSize.connect_same_thread (new_thread_connection, boost::bind (pmf, this, _1, _2, _3, _4));
+       PBD::ThreadCreatedWithRequestSize.connect_same_thread (new_thread_connection, boost::bind (pmf, this, _1, _2, _3));
+
+       /* find pre-registerer threads */
+
+       vector<EventLoop::ThreadBufferMapping> tbm = EventLoop::get_request_buffers_for_target_thread (event_loop_name());
+
+       {
+               Glib::Threads::Mutex::Lock lm (request_buffer_map_lock);
+               for (vector<EventLoop::ThreadBufferMapping>::iterator t = tbm.begin(); t != tbm.end(); ++t) {
+                       request_buffers[t->emitting_thread] = static_cast<RequestBuffer*> (t->request_buffer);
+               }
+       }
 }
 
 template <typename RequestObject> void
-AbstractUI<RequestObject>::register_thread (string target_gui, pthread_t thread_id, string thread_name, uint32_t num_requests)
+AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string thread_name, uint32_t num_requests)
 {
        /* the calling thread wants to register with the thread that runs this
         * UI's event loop, so that it will have its own per-thread queue of
@@ -83,36 +93,39 @@ AbstractUI<RequestObject>::register_thread (string target_gui, pthread_t thread_
         * do so in a realtime-safe manner (no locks).
         */
 
-       DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("in %1 (thread name %4), %2 (%5) wants to register with %3\n", event_loop_name(), thread_name, target_gui, pthread_name(), DEBUG_THREAD_SELF));
-
-       if (target_gui != event_loop_name()) {
-               /* this UI is not the UI that the calling thread is trying to
-                  register with
-               */
-               DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : not the registration target\n", event_loop_name()));
-               return;
-       }
+       DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("in %1 (thread name %4), %2 (%5) wants to register with UIs\n", event_loop_name(), thread_name, pthread_name(), DEBUG_THREAD_SELF));
 
        /* the per_thread_request_buffer is a thread-private variable.
           See pthreads documentation for more on these, but the key
           thing is that it is a variable that as unique value for
-          each thread, guaranteed.
+          each thread, guaranteed. Note that the thread in question
+          is the caller of this function, which is assumed to be the
+          thread from which signals will be emitted that this UI's
+          event loop will catch.
        */
 
        RequestBuffer* b = per_thread_request_buffer.get();
 
-       if (b) {
-               /* thread already registered with this UI
-               */
-               DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : %2 is already registered\n", event_loop_name(), thread_name));
-               return;
-       }
+       if (!b) {
 
-       /* create a new request queue/ringbuffer */
+               /* create a new request queue/ringbuffer */
 
-       DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("create new request buffer for %1 in %2\n", thread_name, event_loop_name()));
+               DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("create new request buffer for %1 in %2\n", thread_name, event_loop_name()));
 
-       b = new RequestBuffer (num_requests, *this);
+               b = new RequestBuffer (num_requests);
+               /* set this thread's per_thread_request_buffer to this new
+                  queue/ringbuffer. remember that only this thread will
+                  get this queue when it calls per_thread_request_buffer.get()
+
+                  the second argument is a function that will be called
+                  when the thread exits, and ensures that the buffer is marked
+                  dead. it will then be deleted during a call to handle_ui_requests()
+               */
+
+               per_thread_request_buffer.set (b);
+       } else {
+               DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : %2 is already registered\n", event_loop_name(), thread_name));
+       }
 
        {
                /* add the new request queue (ringbuffer) to our map
@@ -125,16 +138,6 @@ AbstractUI<RequestObject>::register_thread (string target_gui, pthread_t thread_
                request_buffers[thread_id] = b;
        }
 
-       /* set this thread's per_thread_request_buffer to this new
-          queue/ringbuffer. remember that only this thread will
-          get this queue when it calls per_thread_request_buffer.get()
-
-          the second argument is a function that will be called
-          when the thread exits, and ensures that the buffer is marked
-          dead. it will then be deleted during a call to handle_ui_requests()
-       */
-
-       per_thread_request_buffer.set (b);
 }
 
 template <typename RequestObject> RequestObject*
@@ -229,6 +232,7 @@ AbstractUI<RequestObject>::handle_ui_requests ()
                if ((*i).second->dead) {
                        DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 deleting dead per-thread request buffer for %3 @ %4\n",
                                                                             event_loop_name(), pthread_name(), i->second));
+                       cerr << event_loop_name() << " noticed that a buffer was dead\n";
                        delete (*i).second;
                        RequestBufferMapIterator tmp = i;
                        ++tmp;
@@ -357,6 +361,7 @@ AbstractUI<RequestObject>::send_request (RequestObject *req)
                           single-reader/single-writer semantics
                        */
                        DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send heap request type %3\n", event_loop_name(), pthread_name(), req->type));
+                       cerr << "Send request to " << event_loop_name() << " via LIST from " << pthread_name() << endl;
                        Glib::Threads::Mutex::Lock lm (request_list_lock);
                        request_list.push_back (req);
                }
@@ -407,3 +412,11 @@ AbstractUI<RequestObject>::call_slot (InvalidationRecord* invalidation, const bo
 
        send_request (req);
 }
+
+template<typename RequestObject> void*
+AbstractUI<RequestObject>::request_buffer_factory (uint32_t num_requests)
+{
+       RequestBuffer*  mcr = new RequestBuffer (num_requests);
+       per_thread_request_buffer.set (mcr);
+       return mcr;
+}
index 5491210db7e951401cf69c43a79b21e95c9adb29..78a337fc40bcf601045120ec21724b4954c5bcdd 100644 (file)
@@ -58,20 +58,20 @@ class ABSTRACT_UI_API AbstractUI : public BaseUI
        AbstractUI (const std::string& name);
        virtual ~AbstractUI() {}
 
-       void register_thread (std::string, pthread_t, std::string, uint32_t num_requests);
+       void register_thread (pthread_t, std::string, uint32_t num_requests);
        void call_slot (EventLoop::InvalidationRecord*, const boost::function<void()>&);
         Glib::Threads::Mutex& slot_invalidation_mutex() { return request_buffer_map_lock; }
 
        Glib::Threads::Mutex request_buffer_map_lock;
 
+       static void* request_buffer_factory (uint32_t num_requests);
+
   protected:
        struct RequestBuffer : public PBD::RingBufferNPT<RequestObject> {
                 bool dead;
-                AbstractUI<RequestObject>& ui;
-                RequestBuffer (uint32_t size, AbstractUI<RequestObject>& uir)
+                RequestBuffer (uint32_t size)
                         : PBD::RingBufferNPT<RequestObject> (size)
-                        , dead (false)
-                        , ui (uir) {}
+                       , dead (false) {}
         };
        typedef typename RequestBuffer::rw_vector RequestBufferVector;
 
@@ -105,5 +105,3 @@ class ABSTRACT_UI_API AbstractUI : public BaseUI
 };
 
 #endif /* __pbd_abstract_ui_h__ */
-
-
index 3ea6388f3f6dc34c1a2a589dcc73fd904fa9d0bc..90d72ef47cf7388688924bd38f353297aa32859c 100644 (file)
@@ -21,6 +21,8 @@
 #define __pbd_event_loop_h__
 
 #include <string>
+#include <vector>
+#include <map>
 #include <boost/function.hpp>
 #include <boost/bind.hpp> /* we don't need this here, but anything calling call_slot() probably will, so this is convenient */
 #include <glibmm/threads.h>
@@ -79,9 +81,40 @@ class LIBPBD_API EventLoop
        static EventLoop* get_event_loop_for_thread();
        static void set_event_loop_for_thread (EventLoop* ui);
 
+       struct ThreadBufferMapping {
+               pthread_t emitting_thread;
+               std::string target_thread_name;
+               void* request_buffer;
+       };
+
+       static std::vector<ThreadBufferMapping> get_request_buffers_for_target_thread (const std::string&);
+
+       static void register_request_buffer_factory (const std::string& target_thread_name, void* (*factory) (uint32_t));
+       static void pre_register (const std::string& emitting_thread_name, uint32_t num_requests);
+
   private:
         static Glib::Threads::Private<EventLoop> thread_event_loop;
        std::string _name;
+
+       typedef std::map<std::string,ThreadBufferMapping> ThreadRequestBufferList;
+       static ThreadRequestBufferList thread_buffer_requests;
+       static Glib::Threads::RWLock   thread_buffer_requests_lock;
+
+       struct RequestBufferSupplier {
+
+               /* @param name : name of object/entity that will/may accept
+                  requests from other threads, via a request buffer.
+               */
+               std::string name;
+
+               /* @param factory : a function that can be called (with an
+                  argument specifying the @param number_of_requests) to create and
+                  return a request buffer for communicating with @param name)
+               */
+               void* (*factory)(uint32_t nunber_of_requests);
+       };
+       typedef std::vector<RequestBufferSupplier> RequestBufferSuppliers;
+       static RequestBufferSuppliers request_buffer_suppliers;
 };
 
 }
index a5173ad24d477ffaf6441737aa456296bdb63101..8f70fdac5bc9f10327a605ae4efc2b033ea25af2 100644 (file)
@@ -55,8 +55,8 @@ LIBPBD_API const char* pthread_name ();
 LIBPBD_API void pthread_set_name (const char* name);
 
 namespace PBD {
-       LIBPBD_API extern void notify_gui_about_thread_creation (std::string, pthread_t, std::string, int requests = 256);
-       LIBPBD_API extern PBD::Signal4<void,std::string,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
+       LIBPBD_API extern void notify_event_loops_about_thread_creation (pthread_t, const std::string&, int requests = 256);
+       LIBPBD_API extern PBD::Signal3<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
 }
 
 #endif /* __pbd_pthread_utils__ */
index 1abe6a95fbea6a471c28ad3c2a24ca4fe074147e..7cd25e42b86a23fd6ff1d1332ad6643c31f5282d 100644 (file)
@@ -44,7 +44,7 @@ static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER;
 static Glib::Threads::Private<char> thread_name (free);
 
 namespace PBD {
-       PBD::Signal4<void,std::string, pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
+       PBD::Signal3<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
 }
 
 using namespace PBD;
@@ -58,10 +58,18 @@ static int thread_creator (pthread_t* thread_id, const pthread_attr_t* attr, voi
 #endif
 }
 
+
 void
-PBD::notify_gui_about_thread_creation (std::string target_gui, pthread_t thread, std::string str, int request_count)
+PBD::notify_event_loops_about_thread_creation (pthread_t thread, const std::string& emitting_thread_name, int request_count)
 {
-       ThreadCreatedWithRequestSize (target_gui, thread, str, request_count);
+       /* notify threads that may exist in the future (they may also exist
+        * already, in which case they will catch the
+        * ThreadCreatedWithRequestSize signal)
+        */
+       EventLoop::pre_register (emitting_thread_name, request_count);
+
+       /* notify all existing threads */
+       ThreadCreatedWithRequestSize (thread, emitting_thread_name, request_count);
 }
 
 struct ThreadStartWithName {
@@ -199,4 +207,3 @@ pthread_cancel_one (pthread_t thread)
        pthread_cancel (thread);
        pthread_mutex_unlock (&thread_map_lock);
 }
-
index 8edb3f39a7576f78650ad26294085461107a73c8..71e875419ad777b3d798c2519a003a16f18b8206 100644 (file)
@@ -154,16 +154,24 @@ class LIBCONTROLCP_API ControlProtocol : public PBD::Stateful, public PBD::Scope
 extern "C" {
        class ControlProtocolDescriptor {
        public:
-           const char* name;      /* descriptive */
-           const char* id;        /* unique and version-specific */
-           void*       ptr;       /* protocol can store a value here */
-           void*       module;    /* not for public access */
-           int         mandatory; /* if non-zero, always load and do not make optional */
-           bool        supports_feedback; /* if true, protocol has toggleable feedback mechanism */
-           bool             (*probe)(ControlProtocolDescriptor*);
-           ControlProtocol* (*initialize)(ControlProtocolDescriptor*,Session*);
-           void             (*destroy)(ControlProtocolDescriptor*,ControlProtocol*);
-
+               const char* name;      /* descriptive */
+               const char* id;        /* unique and version-specific */
+               void*       ptr;       /* protocol can store a value here */
+               void*       module;    /* not for public access */
+               int         mandatory; /* if non-zero, always load and do not make optional */
+               bool        supports_feedback; /* if true, protocol has toggleable feedback mechanism */
+               bool             (*probe)(ControlProtocolDescriptor*);
+               ControlProtocol* (*initialize)(ControlProtocolDescriptor*,Session*);
+               void             (*destroy)(ControlProtocolDescriptor*,ControlProtocol*);
+               /* this is required if the control protocol connects to signals
+                  from libardour. they all do. It should allocate a
+                  type-specific request buffer for the calling thread, and
+                  store it in a thread-local location that will be used to
+                  find it when sending the event loop a message
+                  (e.g. call_slot()). It should also return the allocated
+                  buffer as a void*.
+               */
+               void*            (*request_buffer_factory)(uint32_t);
        };
 }
 
index 135943d34f4167f7118e3be45aa59b35088dbc05..f10f70dce538d6e8a1f64d3ba8d7862aa9fa29b2 100644 (file)
@@ -66,7 +66,7 @@ using namespace std;
 
 FaderPort::FaderPort (Session& s)
        : ControlProtocol (s, _("Faderport"))
-       , AbstractUI<FaderPortRequest> ("faderport")
+       , AbstractUI<FaderPortRequest> (name())
        , gui (0)
        , connection_state (ConnectionState (0))
        , _device_active (false)
@@ -210,6 +210,17 @@ FaderPort::~FaderPort ()
        tear_down_gui ();
 }
 
+void*
+FaderPort::request_factory (uint32_t num_requests)
+{
+       /* AbstractUI<T>::request_buffer_factory() is a template method only
+          instantiated in this source module. To provide something visible for
+          use in the interface/descriptor, we have this static method that is
+          template-free.
+       */
+       return request_buffer_factory (num_requests);
+}
+
 void
 FaderPort::start_midi_handling ()
 {
@@ -267,10 +278,10 @@ FaderPort::thread_init ()
 {
        struct sched_param rtparam;
 
-       pthread_set_name (X_("FaderPort"));
+       pthread_set_name (event_loop_name().c_str());
 
-       PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("FaderPort"), 2048);
-       ARDOUR::SessionEvent::create_per_thread_pool (X_("FaderPort"), 128);
+       PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
+       ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
 
        memset (&rtparam, 0, sizeof (rtparam));
        rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
index 6643e22369aaab3f4a4df7166456e9fff4649a15..025cf09e0053efdf4ede3b62e2fa232732c0a8d4 100644 (file)
@@ -85,6 +85,7 @@ class FaderPort : public ARDOUR::ControlProtocol, public AbstractUI<FaderPortReq
           there's no way to know if the device exists or not.
         */
        static bool probe() { return true; }
+       static void* request_factory (uint32_t);
 
        XMLNode& get_state ();
        int set_state (const XMLNode&, int version);
@@ -160,7 +161,7 @@ class FaderPort : public ARDOUR::ControlProtocol, public AbstractUI<FaderPortReq
        std::string get_action (ButtonID, bool on_press, FaderPort::ButtonState = ButtonState (0));
 
        std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
-       
+
   private:
        boost::shared_ptr<ARDOUR::Route> _current_route;
        boost::weak_ptr<ARDOUR::Route> pre_master_route;
index dcfebff1907f22c7ecb4fea35957b8403bbb35d1..e7ea5af396602dfe65c5089939da366cb917a3c8 100644 (file)
@@ -56,6 +56,12 @@ probe_faderport_midi_protocol (ControlProtocolDescriptor* /*descriptor*/)
        return FaderPort::probe ();
 }
 
+static void*
+faderport_request_buffer_factory (uint32_t num_requests)
+{
+       return FaderPort::request_factory (num_requests);
+}
+
 static ControlProtocolDescriptor faderport_midi_descriptor = {
        /*name :              */   "Faderport",
        /*id :                */   "uri://ardour.org/surfaces/faderport:0",
@@ -65,7 +71,8 @@ static ControlProtocolDescriptor faderport_midi_descriptor = {
        /*supports_feedback : */   true,
        /*probe :             */   probe_faderport_midi_protocol,
        /*initialize :        */   new_faderport_midi_protocol,
-       /*destroy :           */   delete_faderport_midi_protocol
+       /*destroy :           */   delete_faderport_midi_protocol,
+       /*request_buffer_factory */ faderport_request_buffer_factory
 };
 
 extern "C" ARDOURSURFACE_API ControlProtocolDescriptor* protocol_descriptor () { return &faderport_midi_descriptor; }
index 1a9760bcbea7461428a9ffccc5d7869a2ef3c2a8..3b0477066083f8efa3b037f4836f2084419b7dd3 100644 (file)
@@ -74,6 +74,12 @@ probe_mackie_protocol (ControlProtocolDescriptor*)
        return MackieControlProtocol::probe();
 }
 
+static void*
+mackie_request_buffer_factory (uint32_t num_requests)
+{
+       return MackieControlProtocol::request_factory (num_requests);
+}
+
 // Field names commented out by JE - 06-01-2010
 static ControlProtocolDescriptor mackie_descriptor = {
        /*name :              */   "Mackie",
@@ -88,8 +94,8 @@ static ControlProtocolDescriptor mackie_descriptor = {
        /*supports_feedback : */   false,
        /*probe :             */   probe_mackie_protocol,
        /*initialize :        */   new_mackie_protocol,
-       /*destroy :           */   delete_mackie_protocol
+       /*destroy :           */   delete_mackie_protocol,
+       /*request_buffer_factory */ mackie_request_buffer_factory
 };
 
-
 extern "C" ARDOURSURFACE_API ControlProtocolDescriptor* protocol_descriptor () { return &mackie_descriptor; }
index dfd3fe63342a1f325d9c9fc42648c934ba908e02..ea5e43d2babc30abb9e37204660bd4c4374c3383 100644 (file)
@@ -105,7 +105,7 @@ bool MackieControlProtocol::probe()
 
 MackieControlProtocol::MackieControlProtocol (Session& session)
        : ControlProtocol (session, X_("Mackie"))
-       , AbstractUI<MackieControlUIRequest> ("mackie")
+       , AbstractUI<MackieControlUIRequest> (name())
        , _current_initial_bank (0)
        , _frame_last (0)
        , _timecode_type (ARDOUR::AnyTime::BBT)
@@ -183,10 +183,10 @@ MackieControlProtocol::thread_init ()
 {
        struct sched_param rtparam;
 
-       pthread_set_name (X_("MackieControl"));
+       pthread_set_name (event_loop_name().c_str());
 
-       PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("MackieControl"), 2048);
-       ARDOUR::SessionEvent::create_per_thread_pool (X_("MackieControl"), 128);
+       PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
+       ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
 
        memset (&rtparam, 0, sizeof (rtparam));
        rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
@@ -2251,3 +2251,14 @@ MackieControlProtocol::global_index (Strip& strip)
 
        return global;
 }
+
+void*
+MackieControlProtocol::request_factory (uint32_t num_requests)
+{
+       /* AbstractUI<T>::request_buffer_factory() is a template method only
+          instantiated in this source module. To provide something visible for
+          use in the interface/descriptor, we have this static method that is
+          template-free.
+       */
+       return request_buffer_factory (num_requests);
+}
index a1cee8ec7f8d90e4295994a1404283a644db2bee..802d5a623531a4d68299bb3ea719090f23df9160 100644 (file)
@@ -178,6 +178,7 @@ class MackieControlProtocol
        */
 
        static bool probe();
+       static void* request_factory (uint32_t);
 
        mutable Glib::Threads::Mutex surfaces_lock;
        typedef std::list<boost::shared_ptr<Mackie::Surface> > Surfaces;
index d26a31788e877144c9b84baf21d348a8d10b6325..35b313945d9e5da08bfd81d2632be49f3b485a1f 100644 (file)
@@ -46,6 +46,12 @@ probe_osc_protocol (ControlProtocolDescriptor* /*descriptor*/)
        return true; // we can always do OSC
 }
 
+static void*
+osc_request_buffer_factory (uint32_t num_requests)
+{
+       return OSC::request_factory (num_requests);
+}
+
 static ControlProtocolDescriptor osc_descriptor = {
        /*name :              */   "Open Sound Control (OSC)",
        /*id :                */   "uri://ardour.org/surfaces/osc:0",
@@ -55,7 +61,8 @@ static ControlProtocolDescriptor osc_descriptor = {
        /*supports_feedback : */   true,
        /*probe :             */   probe_osc_protocol,
        /*initialize :        */   new_osc_protocol,
-       /*destroy :           */   delete_osc_protocol
+       /*destroy :           */   delete_osc_protocol,
+       /*request_buffer_factory */ osc_request_buffer_factory
 };
 
 extern "C" ARDOURSURFACE_API ControlProtocolDescriptor* protocol_descriptor () { return &osc_descriptor; }
index 13261feffa08460c02a628a73e55b7f2d693c1f9..49c8bcc57c78502083b2f020dbb9f035d6976770 100644 (file)
@@ -73,7 +73,7 @@ static void error_callback(int, const char *, const char *)
 
 OSC::OSC (Session& s, uint32_t port)
        : ControlProtocol (s, X_("Open Sound Control (OSC)"))
-       , AbstractUI<OSCUIRequest> ("osc")
+       , AbstractUI<OSCUIRequest> (name())
        , local_server (0)
        , remote_server (0)
        , _port(port)
@@ -96,6 +96,17 @@ OSC::~OSC()
        _instance = 0;
 }
 
+void*
+OSC::request_factory (uint32_t num_requests)
+{
+       /* AbstractUI<T>::request_buffer_factory() is a template method only
+          instantiated in this source module. To provide something visible for
+          use in the interface/descriptor, we have this static method that is
+          template-free.
+       */
+       return request_buffer_factory (num_requests);
+}
+
 void
 OSC::do_request (OSCUIRequest* req)
 {
@@ -226,7 +237,7 @@ OSC::start ()
 void
 OSC::thread_init ()
 {
-       pthread_set_name (X_("OSC"));
+       pthread_set_name (event_loop_name().c_str());
 
        if (_osc_unix_server) {
                Glib::RefPtr<IOSource> src = IOSource::create (lo_server_get_socket_fd (_osc_unix_server), IO_IN|IO_HUP|IO_ERR);
@@ -244,8 +255,8 @@ OSC::thread_init ()
                g_source_ref (remote_server);
        }
 
-       PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("OSC"), 2048);
-       SessionEvent::create_per_thread_pool (X_("OSC"), 128);
+       PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
+       SessionEvent::create_per_thread_pool (event_loop_name(), 128);
 }
 
 int
index 7c24869a0089219185c7ad45c52a9eb96cdce5f3..3718d25525703fff64ff780d052b9a018c471215 100644 (file)
@@ -79,6 +79,8 @@ class OSC : public ARDOUR::ControlProtocol, public AbstractUI<OSCUIRequest>
        int start ();
        int stop ();
 
+       static void* request_factory (uint32_t);
+
   protected:
         void thread_init ();
        void do_request (OSCUIRequest*);
index aa36cdf30071f07aa03a544bcf09da946bfb7135..db5fb55c2af9754c653b1838a85c95e2377eefa5 100644 (file)
@@ -164,7 +164,7 @@ WiimoteControlProtocol::thread_init ()
        pthread_set_name (X_("wiimote"));
 
        // allow to make requests to the GUI and RT thread(s)
-       PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self (), X_("wiimote"), 2048);
+       PBD::notify_event_loops_about_thread_creation (pthread_self (), X_("wiimote"), 2048);
        BasicUI::register_thread ("wiimote");
 
        // connect a Wiimote