summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2015-12-28 10:14:17 -0500
committerPaul Davis <paul@linuxaudiosystems.com>2015-12-28 10:14:17 -0500
commit0d9efc11484c901795ff4e9549a1a39715d0474d (patch)
tree956ab3cd570670bcb1ff68856553f5aec4a8e470
parentdb4834027858b10f313c822c7fb3fad1617f11aa (diff)
redesign cross-thread registration/signalling system
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
-rw-r--r--gtk2_ardour/strip_silence_dialog.cc5
-rw-r--r--libs/ardour/ardour/control_protocol_manager.h1
-rw-r--r--libs/ardour/ardour/midi_ui.h1
-rw-r--r--libs/ardour/audioengine.cc3
-rw-r--r--libs/ardour/control_protocol_manager.cc13
-rw-r--r--libs/ardour/globals.cc19
-rw-r--r--libs/ardour/midi_ui.cc13
-rw-r--r--libs/gtkmm2ext/gtk_ui.cc4
-rw-r--r--libs/pbd/event_loop.cc106
-rw-r--r--libs/pbd/pbd/abstract_ui.cc79
-rw-r--r--libs/pbd/pbd/abstract_ui.h12
-rw-r--r--libs/pbd/pbd/event_loop.h33
-rw-r--r--libs/pbd/pbd/pthread_utils.h4
-rw-r--r--libs/pbd/pthread_utils.cc15
-rw-r--r--libs/surfaces/control_protocol/control_protocol/control_protocol.h28
-rw-r--r--libs/surfaces/faderport/faderport.cc19
-rw-r--r--libs/surfaces/faderport/faderport.h3
-rw-r--r--libs/surfaces/faderport/faderport_interface.cc9
-rw-r--r--libs/surfaces/mackie/interface.cc10
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.cc19
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.h1
-rw-r--r--libs/surfaces/osc/interface.cc9
-rw-r--r--libs/surfaces/osc/osc.cc19
-rw-r--r--libs/surfaces/osc/osc.h2
-rw-r--r--libs/surfaces/wiimote/wiimote.cc2
25 files changed, 348 insertions, 81 deletions
diff --git a/gtk2_ardour/strip_silence_dialog.cc b/gtk2_ardour/strip_silence_dialog.cc
index 71d25d648e..cd41950924 100644
--- a/gtk2_ardour/strip_silence_dialog.cc
+++ b/gtk2_ardour/strip_silence_dialog.cc
@@ -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 ();
diff --git a/libs/ardour/ardour/control_protocol_manager.h b/libs/ardour/ardour/control_protocol_manager.h
index 0c14d2a049..dbbb0c3891 100644
--- a/libs/ardour/ardour/control_protocol_manager.h
+++ b/libs/ardour/ardour/control_protocol_manager.h
@@ -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&);
diff --git a/libs/ardour/ardour/midi_ui.h b/libs/ardour/ardour/midi_ui.h
index 7f57f22e24..2f0f7d3a45 100644
--- a/libs/ardour/ardour/midi_ui.h
+++ b/libs/ardour/ardour/midi_ui.h
@@ -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 ();
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index 3452d1d14c..9c8407e8d7 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -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());
diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc
index 4e2cb7d9f7..6583869fed 100644
--- a/libs/ardour/control_protocol_manager.cc
+++ b/libs/ardour/control_protocol_manager.cc
@@ -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);
+ }
+ }
+}
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index b2ad67d053..9f4a1399b3 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -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);
diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc
index 96304fa051..1d2fe7c7e1 100644
--- a/libs/ardour/midi_ui.cc
+++ b/libs/ardour/midi_ui.cc
@@ -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));
diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc
index ca4d6681dc..538bf8d63f 100644
--- a/libs/gtkmm2ext/gtk_ui.cc
+++ b/libs/gtkmm2ext/gtk_ui.cc
@@ -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 ();
diff --git a/libs/pbd/event_loop.cc b/libs/pbd/event_loop.cc
index 95b43d9038..671e26bedc 100644
--- a/libs/pbd/event_loop.cc
+++ b/libs/pbd/event_loop.cc
@@ -17,9 +17,13 @@
*/
+#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;
+ }
+}
diff --git a/libs/pbd/pbd/abstract_ui.cc b/libs/pbd/pbd/abstract_ui.cc
index d3d3c2e8b1..1514455b42 100644
--- a/libs/pbd/pbd/abstract_ui.cc
+++ b/libs/pbd/pbd/abstract_ui.cc
@@ -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;
+}
diff --git a/libs/pbd/pbd/abstract_ui.h b/libs/pbd/pbd/abstract_ui.h
index 5491210db7..78a337fc40 100644
--- a/libs/pbd/pbd/abstract_ui.h
+++ b/libs/pbd/pbd/abstract_ui.h
@@ -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__ */
-
-
diff --git a/libs/pbd/pbd/event_loop.h b/libs/pbd/pbd/event_loop.h
index 3ea6388f3f..90d72ef47c 100644
--- a/libs/pbd/pbd/event_loop.h
+++ b/libs/pbd/pbd/event_loop.h
@@ -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;
};
}
diff --git a/libs/pbd/pbd/pthread_utils.h b/libs/pbd/pbd/pthread_utils.h
index a5173ad24d..8f70fdac5b 100644
--- a/libs/pbd/pbd/pthread_utils.h
+++ b/libs/pbd/pbd/pthread_utils.h
@@ -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__ */
diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc
index 1abe6a95fb..7cd25e42b8 100644
--- a/libs/pbd/pthread_utils.cc
+++ b/libs/pbd/pthread_utils.cc
@@ -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);
}
-
diff --git a/libs/surfaces/control_protocol/control_protocol/control_protocol.h b/libs/surfaces/control_protocol/control_protocol/control_protocol.h
index 8edb3f39a7..71e875419a 100644
--- a/libs/surfaces/control_protocol/control_protocol/control_protocol.h
+++ b/libs/surfaces/control_protocol/control_protocol/control_protocol.h
@@ -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);
};
}
diff --git a/libs/surfaces/faderport/faderport.cc b/libs/surfaces/faderport/faderport.cc
index 135943d34f..f10f70dce5 100644
--- a/libs/surfaces/faderport/faderport.cc
+++ b/libs/surfaces/faderport/faderport.cc
@@ -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 */
diff --git a/libs/surfaces/faderport/faderport.h b/libs/surfaces/faderport/faderport.h
index 6643e22369..025cf09e00 100644
--- a/libs/surfaces/faderport/faderport.h
+++ b/libs/surfaces/faderport/faderport.h
@@ -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;
diff --git a/libs/surfaces/faderport/faderport_interface.cc b/libs/surfaces/faderport/faderport_interface.cc
index dcfebff190..e7ea5af396 100644
--- a/libs/surfaces/faderport/faderport_interface.cc
+++ b/libs/surfaces/faderport/faderport_interface.cc
@@ -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; }
diff --git a/libs/surfaces/mackie/interface.cc b/libs/surfaces/mackie/interface.cc
index 1a9760bcbe..3b04770660 100644
--- a/libs/surfaces/mackie/interface.cc
+++ b/libs/surfaces/mackie/interface.cc
@@ -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; }
diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc
index dfd3fe6334..ea5e43d2ba 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol.cc
@@ -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);
+}
diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h
index a1cee8ec7f..802d5a6235 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.h
+++ b/libs/surfaces/mackie/mackie_control_protocol.h
@@ -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;
diff --git a/libs/surfaces/osc/interface.cc b/libs/surfaces/osc/interface.cc
index d26a31788e..35b313945d 100644
--- a/libs/surfaces/osc/interface.cc
+++ b/libs/surfaces/osc/interface.cc
@@ -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; }
diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc
index 13261feffa..49c8bcc57c 100644
--- a/libs/surfaces/osc/osc.cc
+++ b/libs/surfaces/osc/osc.cc
@@ -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
diff --git a/libs/surfaces/osc/osc.h b/libs/surfaces/osc/osc.h
index 7c24869a00..3718d25525 100644
--- a/libs/surfaces/osc/osc.h
+++ b/libs/surfaces/osc/osc.h
@@ -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*);
diff --git a/libs/surfaces/wiimote/wiimote.cc b/libs/surfaces/wiimote/wiimote.cc
index aa36cdf300..db5fb55c2a 100644
--- a/libs/surfaces/wiimote/wiimote.cc
+++ b/libs/surfaces/wiimote/wiimote.cc
@@ -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