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:
parent
db48340278
commit
0d9efc1148
|
@ -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 ();
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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 ();
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user