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 | |
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')
-rw-r--r-- | libs/pbd/base_ui.cc | 85 | ||||
-rw-r--r-- | libs/pbd/crossthread.cc | 98 | ||||
-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 | ||||
-rw-r--r-- | libs/pbd/pthread_utils.cc | 45 | ||||
-rw-r--r-- | libs/pbd/wscript | 1 |
9 files changed, 292 insertions, 194 deletions
diff --git a/libs/pbd/base_ui.cc b/libs/pbd/base_ui.cc index 015951f118..259a51d954 100644 --- a/libs/pbd/base_ui.cc +++ b/libs/pbd/base_ui.cc @@ -33,36 +33,24 @@ using namespace std; using namespace PBD; +using namespace Glib; -uint32_t BaseUI::rt_bit = 1; +uint64_t BaseUI::rt_bit = 1; BaseUI::RequestType BaseUI::CallSlot = BaseUI::new_request_type(); -BaseUI::BaseUI (string str, bool with_signal_pipe) - : _name (str) +BaseUI::BaseUI (const string& str) + : run_loop_thread (0) + , _name (str) { - /* odd pseudo-singleton semantics */ - base_ui_instance = this; - signal_pipe[0] = -1; - signal_pipe[1] = -1; + request_channel.ios()->connect (sigc::mem_fun (*this, &BaseUI::request_handler)); - if (with_signal_pipe) { - if (setup_signal_pipe ()) { - throw failed_constructor (); - } - } + /* derived class must set _ok */ } BaseUI::~BaseUI() { - if (signal_pipe[0] >= 0) { - close (signal_pipe[0]); - } - - if (signal_pipe[1] >= 0) { - close (signal_pipe[1]); - } } BaseUI::RequestType @@ -78,32 +66,53 @@ BaseUI::new_request_type () return rt; } -int -BaseUI::setup_signal_pipe () +void +BaseUI::main_thread () +{ + thread_init (); + _main_loop->run (); +} + +void +BaseUI::run () { - /* setup the pipe that other threads send us notifications/requests - through. + /* to be called by UI's that need/want their own distinct, self-created event loop thread. + Derived classes should have set up a handler for IO on request_channel.ios() */ - if (pipe (signal_pipe)) { - error << string_compose (_("%1-UI: cannot create error signal pipe (%2)"), _name, ::strerror (errno)) - << endmsg; + _main_loop = MainLoop::create (MainContext::create()); + request_channel.ios()->attach (_main_loop->get_context()); + run_loop_thread = Thread::create (mem_fun (*this, &BaseUI::main_thread), true); +} - return -1; - } +void +BaseUI::quit () +{ + _main_loop->quit (); + run_loop_thread->join (); +} + +bool +BaseUI::request_handler (Glib::IOCondition ioc) +{ + /* check the transport request pipe */ - if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) { - error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal read pipe (%2)"), _name, ::strerror (errno)) - << endmsg; - return -1; + if (ioc & ~IO_IN) { + _main_loop->quit (); } - if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) { - error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, ::strerror (errno)) - << endmsg; - return -1; + if (ioc & IO_IN) { + request_channel.drain (); + + /* there may been an error. we'd rather handle requests first, + and then get IO_HUP or IO_ERR on the next loop. + */ + + /* handle requests */ + + handle_ui_requests (); } - return 0; + return true; } - + diff --git a/libs/pbd/crossthread.cc b/libs/pbd/crossthread.cc new file mode 100644 index 0000000000..1465505b56 --- /dev/null +++ b/libs/pbd/crossthread.cc @@ -0,0 +1,98 @@ +/* + Copyright (C) 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + 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 <cstdlib> +#include <cerrno> +#include <cstring> +#include <fcntl.h> +#include <unistd.h> + +#include "pbd/error.h" +#include "pbd/crossthread.h" + +using namespace std; +using namespace PBD; +using namespace Glib; + +CrossThreadChannel::CrossThreadChannel () +{ + fds[0] = -1; + fds[1] = -1; + + if (pipe (fds)) { + error << "cannot create x-thread pipe for read (%2)" << ::strerror (errno) << endmsg; + return; + } + + if (fcntl (fds[0], F_SETFL, O_NONBLOCK)) { + error << "cannot set non-blocking mode for x-thread pipe (read) (" << ::strerror (errno) << ')' << endmsg; + return; + } + + if (fcntl (fds[1], F_SETFL, O_NONBLOCK)) { + error << "cannot set non-blocking mode for x-thread pipe (write) (%2)" << ::strerror (errno) << ')' << endmsg; + return; + } + +} + +CrossThreadChannel::~CrossThreadChannel () +{ + _ios->destroy (); + + if (fds[0] >= 0) { + close (fds[0]); + fds[0] = -1; + } + + if (fds[1] >= 0) { + close (fds[1]); + fds[1] = -1; + } +} + +void +CrossThreadChannel::wakeup () +{ + char c = 0; + ::write (fds[1], &c, 1); +} + +RefPtr<IOSource> +CrossThreadChannel::ios () +{ + if (!_ios) { + _ios = IOSource::create (fds[0], IOCondition(IO_IN|IO_PRI|IO_ERR|IO_HUP|IO_NVAL)); + } + return _ios; +} + +void +CrossThreadChannel::drain () +{ + drain (fds[0]); +} + +void +CrossThreadChannel::drain (int fd) +{ + /* drain selectable fd */ + char buf[64]; + while (::read (fd, buf, sizeof (buf)) > 0); +} 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__ */ diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index 69270a767b..68082e6136 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -36,8 +36,8 @@ static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t gui_notify_lock = PTHREAD_MUTEX_INITIALIZER; namespace PBD { - sigc::signal<void,pthread_t> ThreadLeaving; - sigc::signal<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize; + sigc::signal<void,pthread_t> ThreadLeaving; + sigc::signal<void,std::string, pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize; } using namespace PBD; @@ -52,10 +52,10 @@ static int thread_creator (pthread_t* thread_id, const pthread_attr_t* attr, voi } void -PBD::notify_gui_about_thread_creation (pthread_t thread, std::string str, int request_count) +PBD::notify_gui_about_thread_creation (std::string target_gui, pthread_t thread, std::string str, int request_count) { pthread_mutex_lock (&gui_notify_lock); - ThreadCreatedWithRequestSize (thread, str, request_count); + ThreadCreatedWithRequestSize (target_gui, thread, str, request_count); pthread_mutex_unlock (&gui_notify_lock); } @@ -68,35 +68,27 @@ PBD::notify_gui_about_thread_exit (pthread_t thread) } int -pthread_create_and_store (string name, pthread_t *thread, pthread_attr_t *attr, void * (*start_routine)(void *), void * arg) +pthread_create_and_store (string name, pthread_t *thread, void * (*start_routine)(void *), void * arg) { - int ret; - pthread_attr_t default_attr; - bool use_default_attr = (attr == NULL); + int ret; - if (use_default_attr) { - // set default stack size to sensible default for memlocking - pthread_attr_init(&default_attr); - pthread_attr_setstacksize(&default_attr, 500000); - attr = &default_attr; - } + // set default stack size to sensible default for memlocking + pthread_attr_init(&default_attr); + pthread_attr_setstacksize(&default_attr, 500000); - if ((ret = thread_creator (thread, attr, start_routine, arg)) == 0) { + if ((ret = thread_creator (thread, &default_attr, start_routine, arg)) == 0) { std::pair<string,pthread_t> newpair; newpair.first = name; newpair.second = *thread; pthread_mutex_lock (&thread_map_lock); all_threads.insert (newpair); - pthread_mutex_unlock (&thread_map_lock); } - if (use_default_attr) { - pthread_attr_destroy(&default_attr); - } - + pthread_attr_destroy(&default_attr); + return ret; } @@ -132,19 +124,6 @@ pthread_kill_all (int signum) } void -pthread_cancel_all () -{ - pthread_mutex_lock (&thread_map_lock); - for (ThreadMap::iterator i = all_threads.begin(); i != all_threads.end(); ++i) { - if (i->second != pthread_self()) { - pthread_cancel (i->second); - } - } - all_threads.clear(); - pthread_mutex_unlock (&thread_map_lock); -} - -void pthread_cancel_one (pthread_t thread) { pthread_mutex_lock (&thread_map_lock); diff --git a/libs/pbd/wscript b/libs/pbd/wscript index 122f38da79..e114cbe1ae 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -57,6 +57,7 @@ def build(bld): command.cc convert.cc controllable.cc + crossthread.cc enumwriter.cc dmalloc.cc error.cc |