diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2009-12-09 03:05:14 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2009-12-09 03:05:14 +0000 |
commit | c38e02285fda1fd7966c9e4ad85994445247e6a6 (patch) | |
tree | a5f46d4350b8df3e0a74558169c696cbb837ce7f /libs/pbd/pbd | |
parent | 90f95df20707995e267bd624b28980cfd9200bed (diff) |
major design changes: use glib event loop for MIDI thread/UI; rework design of BaseUI and AbstractUI; solo & mute are both temporarily broken; OSC control up next; may segfault during exit
git-svn-id: svn://localhost/ardour2/branches/3.0@6328 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/pbd/pbd')
-rw-r--r-- | libs/pbd/pbd/abstract_ui.cc | 129 | ||||
-rw-r--r-- | libs/pbd/pbd/abstract_ui.h | 33 | ||||
-rw-r--r-- | libs/pbd/pbd/base_ui.h | 33 | ||||
-rw-r--r-- | libs/pbd/pbd/crossthread.h | 53 | ||||
-rw-r--r-- | libs/pbd/pbd/pthread_utils.h | 9 |
5 files changed, 134 insertions, 123 deletions
diff --git a/libs/pbd/pbd/abstract_ui.cc b/libs/pbd/pbd/abstract_ui.cc index 25c198774c..07f6d3a4b1 100644 --- a/libs/pbd/pbd/abstract_ui.cc +++ b/libs/pbd/pbd/abstract_ui.cc @@ -1,5 +1,6 @@ #include <unistd.h> +#include "pbd/stacktrace.h" #include "pbd/abstract_ui.h" #include "pbd/pthread_utils.h" #include "pbd/failed_constructor.h" @@ -9,81 +10,65 @@ using namespace std; template <typename RequestObject> -AbstractUI<RequestObject>::AbstractUI (string name, bool with_signal_pipes) - : BaseUI (name, with_signal_pipes) +AbstractUI<RequestObject>::AbstractUI (const string& name) + : BaseUI (name) { - if (pthread_key_create (&thread_request_buffer_key, 0)) { - cerr << _("cannot create thread request buffer key") << endl; - throw failed_constructor(); - } - - PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &AbstractUI<RequestObject>::register_thread_with_request_count)); + PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &AbstractUI<RequestObject>::register_thread)); } template <typename RequestObject> void -AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string name) +AbstractUI<RequestObject>::register_thread (string target_gui, pthread_t thread_id, string /*thread_name*/, uint32_t num_requests) { - register_thread_with_request_count (thread_id, name, 256); -} + if (target_gui != name()) { + return; + } -template <typename RequestObject> void -AbstractUI<RequestObject>::register_thread_with_request_count (pthread_t thread_id, string /*thread_name*/, uint32_t num_requests) -{ RequestBuffer* b = new RequestBuffer (num_requests); { - Glib::Mutex::Lock lm (request_buffer_map_lock); + Glib::Mutex::Lock lm (request_buffer_map_lock); request_buffers[thread_id] = b; } - pthread_setspecific (thread_request_buffer_key, b); + per_thread_request_buffer.set (b); } template <typename RequestObject> RequestObject* AbstractUI<RequestObject>::get_request (RequestType rt) { - RequestBuffer* rbuf = static_cast<RequestBuffer*>(pthread_getspecific (thread_request_buffer_key)); - - if (rbuf == 0) { - /* Cannot happen, but if it does we can't use the error reporting mechanism */ - cerr << _("programming error: ") - << string_compose ("no %1-UI request buffer found for thread %2", name(), pthread_name()) - << endl; - abort (); - } - + RequestBuffer* rbuf = per_thread_request_buffer.get (); RequestBufferVector vec; - vec.buf[0] = 0; - vec.buf[1] = 0; - - rbuf->get_write_vector (&vec); - if (vec.len[0] == 0) { - if (vec.len[1] == 0) { - cerr << string_compose ("no space in %1-UI request buffer for thread %2", name(), pthread_name()) - << endl; + if (rbuf != 0) { + /* we have a per-thread FIFO, use it */ + + rbuf->get_write_vector (&vec); + + if (vec.len[0] == 0) { return 0; - } else { - vec.buf[1]->type = rt; - return vec.buf[1]; } - } else { + vec.buf[0]->type = rt; return vec.buf[0]; } + + RequestObject* req = new RequestObject; + req->type = rt; + return req; } template <typename RequestObject> void AbstractUI<RequestObject>::handle_ui_requests () { RequestBufferMapIterator i; + RequestBufferVector vec; + + /* per-thread buffers first */ request_buffer_map_lock.lock (); for (i = request_buffers.begin(); i != request_buffers.end(); ++i) { - RequestBufferVector vec; - while (true) { /* we must process requests 1 by 1 because @@ -110,6 +95,22 @@ AbstractUI<RequestObject>::handle_ui_requests () } request_buffer_map_lock.unlock (); + + /* and now, the generic request buffer. same rules as above apply */ + + Glib::Mutex::Lock lm (request_list_lock); + + while (!request_list.empty()) { + RequestObject* req = request_list.front (); + request_list.pop_front (); + lm.release (); + + do_request (req); + + delete req; + + lm.acquire(); + } } template <typename RequestObject> void @@ -118,31 +119,41 @@ AbstractUI<RequestObject>::send_request (RequestObject *req) if (base_instance() == 0) { return; /* XXX is this the right thing to do ? */ } - - if (caller_is_ui_thread()) { - // cerr << "GUI thread sent request " << req << " type = " << req->type << endl; + + if (caller_is_self ()) { do_request (req); } else { - RequestBuffer* rbuf = static_cast<RequestBuffer*> (pthread_getspecific (thread_request_buffer_key)); + RequestBuffer* rbuf = per_thread_request_buffer.get (); - if (rbuf == 0) { - /* can't use the error system to report this, because this - thread isn't registered! + if (rbuf != 0) { + rbuf->increment_write_ptr (1); + } else { + /* no per-thread buffer, so just use a list with a lock so that it remains + single-reader/single-writer semantics */ - cerr << _("programming error: ") - << string_compose ("AbstractUI::send_request() called from %1 (%2), but no request buffer exists for that thread", name(), pthread_name()) - << endl; - abort (); + Glib::Mutex::Lock lm (request_list_lock); + request_list.push_back (req); } - - // cerr << "thread " << pthread_self() << " sent request " << req << " type = " << req->type << endl; - rbuf->increment_write_ptr (1); - - if (signal_pipe[1] >= 0) { - const char c = 0; - write (signal_pipe[1], &c, 1); - } + request_channel.wakeup (); } } +template<typename RequestObject> void +AbstractUI<RequestObject>::call_slot (sigc::slot<void> elSlot) +{ + if (caller_is_self()) { + elSlot (); + return; + } + + RequestObject *req = get_request (BaseUI::CallSlot); + + if (req == 0) { + return; + } + + req->the_slot = elSlot; + send_request (req); +} + diff --git a/libs/pbd/pbd/abstract_ui.h b/libs/pbd/pbd/abstract_ui.h index adb9aabf0e..daa1b83e3c 100644 --- a/libs/pbd/pbd/abstract_ui.h +++ b/libs/pbd/pbd/abstract_ui.h @@ -1,5 +1,5 @@ /* - Copyright (C) 1998-99 Paul Barton-Davis + Copyright (C) 1998-2009 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,38 +34,29 @@ class Touchable; -template <class RequestObject> +template<typename RequestObject> class AbstractUI : public BaseUI { public: - AbstractUI (std::string name, bool with_signal_pipe); + AbstractUI (const std::string& name); virtual ~AbstractUI() {} - virtual bool caller_is_ui_thread() = 0; - - void call_slot (sigc::slot<void> el_slot) { - RequestObject *req = get_request (BaseUI::CallSlot); - - if (req == 0) { - return; - } - - req->slot = el_slot; - send_request (req); - } - - void register_thread (pthread_t, std::string); - void register_thread_with_request_count (pthread_t, std::string, uint32_t num_requests); + void register_thread (std::string, pthread_t, std::string, uint32_t num_requests); + void call_slot (sigc::slot<void> el_slot); protected: typedef RingBufferNPT<RequestObject> RequestBuffer; typedef typename RequestBuffer::rw_vector RequestBufferVector; typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator; - - Glib::Mutex request_buffer_map_lock; typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap; + + Glib::Mutex request_buffer_map_lock; RequestBufferMap request_buffers; - pthread_key_t thread_request_buffer_key; + Glib::Private<RequestBuffer> per_thread_request_buffer; + + Glib::Mutex request_list_lock; + std::list<RequestObject*> request_list; + RequestObject* get_request (RequestType); void handle_ui_requests (); void send_request (RequestObject *); diff --git a/libs/pbd/pbd/base_ui.h b/libs/pbd/pbd/base_ui.h index 0928512841..614873e5d1 100644 --- a/libs/pbd/pbd/base_ui.h +++ b/libs/pbd/pbd/base_ui.h @@ -26,13 +26,22 @@ #include <sigc++/slot.h> #include <sigc++/trackable.h> +#include <glibmm/thread.h> +#include <glibmm/main.h> + +#include "pbd/crossthread.h" + class BaseUI : virtual public sigc::trackable { public: - BaseUI (std::string name, bool with_signal_pipes); + BaseUI (const std::string& name); virtual ~BaseUI(); BaseUI* base_instance() { return base_ui_instance; } + Glib::RefPtr<Glib::MainLoop> main_loop() const { return _main_loop; } + Glib::Thread* event_loop_thread() const { return run_loop_thread; } + bool caller_is_self () const { return Glib::Thread::self() == run_loop_thread; } + std::string name() const { return _name; } bool ok() const { return _ok; } @@ -49,17 +58,31 @@ class BaseUI : virtual public sigc::trackable { static RequestType new_request_type(); static RequestType CallSlot; + void run (); + void quit (); + + virtual void call_slot (sigc::slot<void> theSlot) = 0; + protected: - int signal_pipe[2]; + CrossThreadChannel request_channel; bool _ok; + Glib::RefPtr<Glib::MainLoop> _main_loop; + Glib::Thread* run_loop_thread; + + virtual void thread_init () {}; + bool request_handler (Glib::IOCondition); + + virtual void handle_ui_requests () = 0; + private: std::string _name; BaseUI* base_ui_instance; + + static uint64_t rt_bit; - static uint32_t rt_bit; - - int setup_signal_pipe (); + int setup_request_pipe (); + void main_thread (); }; #endif /* __pbd_base_ui_h__ */ diff --git a/libs/pbd/pbd/crossthread.h b/libs/pbd/pbd/crossthread.h index c63c863f38..f2fb4aa469 100644 --- a/libs/pbd/pbd/crossthread.h +++ b/libs/pbd/pbd/crossthread.h @@ -20,38 +20,25 @@ #ifndef __pbd__crossthread_h__ #define __pbd__crossthread_h__ -#include "pbd/abstract_ui.h" -#include <sigc++/sigc++.h> -#include <pthread.h> - -template<class RequestType> -void -call_slot_from_thread_or_dispatch_it (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot) -{ - /* when called, this function will determine whether the calling thread - is the same as thread specified by the first argument. if it is, - the we execute the slot. if not, we ask the interface given by the second - argument to call the slot. - */ - - if (pthread_self() == thread_id) { - theSlot (); - } else { - ui.call_slot (theSlot); - } -} - -template<class RequestType> -sigc::slot<void> -crossthread_safe (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot) -{ - /* this function returns a slot that will ensure that theSlot is either - called by the specified thread or passed to the interface via - AbstractUI::call_slot(). - */ - - return sigc::bind (sigc::ptr_fun (call_slot_from_thread_or_dispatch_it<RequestType>), - thread_id, ui, theSlot); -} +#include <glibmm/main.h> + +class CrossThreadChannel { + public: + CrossThreadChannel(); + ~CrossThreadChannel(); + + void wakeup(); + int selectable() const { return fds[0]; } + + void drain (); + static void drain (int fd); + + Glib::RefPtr<Glib::IOSource> ios(); + bool ok() const { return fds[0] >= 0 && fds[1] >= 0; } + + private: + Glib::RefPtr<Glib::IOSource> _ios; // lazily constructed + int fds[2]; +}; #endif /* __pbd__crossthread_h__ */ diff --git a/libs/pbd/pbd/pthread_utils.h b/libs/pbd/pbd/pthread_utils.h index dd91e0a2b1..15b37662d5 100644 --- a/libs/pbd/pbd/pthread_utils.h +++ b/libs/pbd/pbd/pthread_utils.h @@ -27,19 +27,18 @@ #include <sigc++/sigc++.h> -int pthread_create_and_store (std::string name, pthread_t *thread, pthread_attr_t *attr, void * (*start_routine)(void *), void * arg); +int pthread_create_and_store (std::string name, pthread_t *thread, void * (*start_routine)(void *), void * arg); void pthread_cancel_one (pthread_t thread); void pthread_kill_all (int signum); -void pthread_cancel_all (); void pthread_exit_pbd (void* status); std::string pthread_name (); namespace PBD { - extern void notify_gui_about_thread_creation (pthread_t, std::string, int requests = 256); + extern void notify_gui_about_thread_creation (std::string, pthread_t, std::string, int requests = 256); extern void notify_gui_about_thread_exit (pthread_t); - extern sigc::signal<void,pthread_t> ThreadLeaving; - extern sigc::signal<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize; + extern sigc::signal<void,pthread_t> ThreadLeaving; + extern sigc::signal<void,std::string,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize; } #endif /* __pbd_pthread_utils__ */ |