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
This commit is contained in:
Paul Davis 2015-12-28 10:14:17 -05:00
parent db48340278
commit 0d9efc1148
25 changed files with 348 additions and 81 deletions

View 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 ();

View 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&);

View 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 ();

View 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());

View 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);
}
}
}

View 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);

View 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));

View 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 ();

View File

@ -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;
}
}

View 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,37 +93,40 @@ 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
if (!b) {
/* 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()));
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));
return;
}
/* 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()));
b = new RequestBuffer (num_requests, *this);
{
/* add the new request queue (ringbuffer) to our map
so that we can iterate over it when the time is right.
@ -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;
}

View 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__ */

View 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;
};
}

View 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__ */

View 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);
}

View 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);
};
}

View 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 */

View 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;

View 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; }

View 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; }

View 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);
}

View 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;

View 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; }

View 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

View 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*);

View 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