From 9a30bfd0c602ee08142374b2a409d28573530fc6 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 1 Mar 2015 20:55:39 +0100 Subject: use Xthreads in session butler. (hopefully) fixes export randomly stalling on windows: dequeue_request() was a single request (no queue) on Windows. Butler::queue_request() is called -> Butler goes to work.. -> while working, another request is queued -> butler never sees this -> deadlock during Freewheeling/Export wait_until_finished() waits for the 2nd request to be handled, and never returns. --- libs/ardour/ardour/butler.h | 26 ++-------- libs/ardour/butler.cc | 119 +++----------------------------------------- 2 files changed, 11 insertions(+), 134 deletions(-) diff --git a/libs/ardour/ardour/butler.h b/libs/ardour/ardour/butler.h index 949668dab3..94b6fb876b 100644 --- a/libs/ardour/ardour/butler.h +++ b/libs/ardour/ardour/butler.h @@ -28,6 +28,7 @@ #include "pbd/glib_semaphore.h" #endif +#include "pbd/crossthread.h" #include "pbd/ringbuffer.h" #include "pbd/pool.h" #include "ardour/libardour_visibility.h" @@ -86,38 +87,17 @@ class LIBARDOUR_API Butler : public SessionHandleRef uint32_t midi_dstream_buffer_size; RingBuffer pool_trash; -#ifdef PLATFORM_WINDOWS - PBD::atomic_counter m_request_state; - PBD::GlibSemaphore m_request_sem; -#else - int request_pipe[2]; -#endif - private: void empty_pool_trash (); void config_changed (std::string); -#ifndef PLATFORM_WINDOWS - int setup_request_pipe (); -#endif - - /** - * return true if there are requests to be processed - */ - bool wait_for_requests (); - - /** - * Remove request from butler request queue - * - * return true if there was another request and req is valid - */ - bool dequeue_request (Request::Type& req); - /** * Add request to butler thread request queue */ void queue_request (Request::Type r); + CrossThreadChannel _xthread; + }; } // namespace ARDOUR diff --git a/libs/ardour/butler.cc b/libs/ardour/butler.cc index c33b6e333d..cd00965062 100644 --- a/libs/ardour/butler.cc +++ b/libs/ardour/butler.cc @@ -49,6 +49,7 @@ Butler::Butler(Session& s) , audio_dstream_playback_buffer_size(0) , midi_dstream_buffer_size(0) , pool_trash(16) + , _xthread (true) { g_atomic_int_set(&should_do_transport_work, 0); SessionEvent::pool->set_trash (&pool_trash); @@ -74,31 +75,6 @@ Butler::config_changed (std::string p) } } -#ifndef PLATFORM_WINDOWS -int -Butler::setup_request_pipe () -{ - if (pipe (request_pipe)) { - error << string_compose(_("Cannot create transport request signal pipe (%1)"), - strerror (errno)) << endmsg; - return -1; - } - - if (fcntl (request_pipe[0], F_SETFL, O_NONBLOCK)) { - error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"), - strerror (errno)) << endmsg; - return -1; - } - - if (fcntl (request_pipe[1], F_SETFL, O_NONBLOCK)) { - error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"), - strerror (errno)) << endmsg; - return -1; - } - return 0; -} -#endif - int Butler::start_thread() { @@ -118,10 +94,6 @@ Butler::start_thread() should_run = false; -#ifndef PLATFORM_WINDOWS - if (setup_request_pipe() != 0) return -1; -#endif - if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) { error << _("Session: could not create butler thread") << endmsg; return -1; @@ -151,69 +123,6 @@ Butler::_thread_work (void* arg) return ((Butler *) arg)->thread_work (); } -bool -Butler::wait_for_requests () -{ -#ifndef PLATFORM_WINDOWS - struct pollfd pfd[1]; - - pfd[0].fd = request_pipe[0]; - pfd[0].events = POLLIN|POLLERR|POLLHUP; - - while(true) { - if (poll (pfd, 1, -1) < 0) { - - if (errno == EINTR) { - continue; - } - - error << string_compose (_("poll on butler request pipe failed (%1)"), - strerror (errno)) - << endmsg; - break; - } - - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler awake at @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time())); - - if (pfd[0].revents & ~POLLIN) { - error << string_compose (_("Error on butler thread request pipe: fd=%1 err=%2"), pfd[0].fd, pfd[0].revents) << endmsg; - break; - } - - if (pfd[0].revents & POLLIN) { - return true; - } - } - return false; -#else - m_request_sem.wait (); - return true; -#endif -} - -bool -Butler::dequeue_request (Request::Type& r) -{ -#ifndef PLATFORM_WINDOWS - char req; - size_t nread = ::read (request_pipe[0], &req, sizeof (req)); - if (nread == 1) { - r = (Request::Type) req; - return true; - } else if (nread == 0) { - return false; - } else if (errno == EAGAIN) { - return false; - } else { - fatal << _("Error reading from butler request pipe") << endmsg; - abort(); /*NOTREACHED*/ - } -#else - r = (Request::Type) m_request_state.get(); -#endif - return false; -} - void * Butler::thread_work () { @@ -227,16 +136,12 @@ Butler::thread_work () if(!disk_work_outstanding) { DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 butler waits for requests @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time())); - if (wait_for_requests ()) { - Request::Type req; - - /* empty the pipe of all current requests */ -#ifdef PLATFORM_WINDOWS - dequeue_request (req); -#else - while (dequeue_request(req)) { -#endif - switch (req) { + + char msg; + /* empty the pipe of all current requests */ + if (_xthread.receive (msg, true) >= 0) { + Request::Type req = (Request::Type) msg; + switch (req) { case Request::Run: DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to run @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time())); @@ -256,10 +161,7 @@ Butler::thread_work () default: break; - } -#ifndef PLATFORM_WINDOWS } -#endif } } @@ -425,13 +327,8 @@ Butler::schedule_transport_work () void Butler::queue_request (Request::Type r) { -#ifndef PLATFORM_WINDOWS char c = r; - (void) ::write (request_pipe[1], &c, 1); -#else - m_request_state.set (r); - m_request_sem.post (); -#endif + _xthread.deliver (c); } void -- cgit v1.2.3