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.
This commit is contained in:
Robin Gareus
2015-03-01 20:55:39 +01:00
parent d7727a77e0
commit 9a30bfd0c6
2 changed files with 10 additions and 133 deletions

View 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

View 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