use Xthreads in session butler.
authorRobin Gareus <robin@gareus.org>
Sun, 1 Mar 2015 19:55:39 +0000 (20:55 +0100)
committerRobin Gareus <robin@gareus.org>
Sun, 1 Mar 2015 19:55:39 +0000 (20:55 +0100)
(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
libs/ardour/butler.cc

index 949668dab3d1f4d82c991861cf92511d1384d548..94b6fb876bd056311a368879e6f2dea61e03a4e6 100644 (file)
@@ -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<CrossThreadPool*> 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
index c33b6e333dc4e6e928d0c367c4cd8f9242cb7637..cd009650625220be4e9998eea8adf5e15c6bca7e 100644 (file)
@@ -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