mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-12-15 08:56:34 -05:00
x11: gracefully handle X11 connection error
Perform X11 connection recovery via XSetIOErrorExitHandler mechanism. Implementation is largely inspired by this change to GNOME/mutter https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1447 Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/441>
This commit is contained in:
parent
28f646fd87
commit
b6396dbe9c
8 changed files with 81 additions and 6 deletions
12
configure.ac
12
configure.ac
|
|
@ -633,8 +633,14 @@ AC_ARG_ENABLE([x11],
|
||||||
AS_HELP_STRING([--disable-x11],[Disable optional X11 support]))
|
AS_HELP_STRING([--disable-x11],[Disable optional X11 support]))
|
||||||
|
|
||||||
AS_IF([test "x$enable_x11" != "xno"],
|
AS_IF([test "x$enable_x11" != "xno"],
|
||||||
[PKG_CHECK_MODULES(X11, [ x11-xcb xcb >= 1.6 ice sm xtst ], HAVE_X11=1, HAVE_X11=0)],
|
[
|
||||||
HAVE_X11=0)
|
PKG_CHECK_MODULES(X11, [ x11-xcb xcb >= 1.6 ice sm xtst ], HAVE_X11=1, HAVE_X11=0)
|
||||||
|
AC_CHECK_LIB(X11, XSetIOErrorExitHandler, [HAVE_XSETIOERROREXITHANDLER=yes], [HAVE_XSETIOERROREXITHANDLER=no])
|
||||||
|
],
|
||||||
|
[
|
||||||
|
HAVE_X11=0
|
||||||
|
HAVE_XSETIOERROREXITHANDLER=no
|
||||||
|
])
|
||||||
|
|
||||||
AS_IF([test "x$enable_x11" = "xyes" && test "x$HAVE_X11" = "x0"],
|
AS_IF([test "x$enable_x11" = "xyes" && test "x$HAVE_X11" = "x0"],
|
||||||
[AC_MSG_ERROR([*** X11 not found])])
|
[AC_MSG_ERROR([*** X11 not found])])
|
||||||
|
|
@ -642,6 +648,7 @@ AS_IF([test "x$enable_x11" = "xyes" && test "x$HAVE_X11" = "x0"],
|
||||||
AC_SUBST(HAVE_X11)
|
AC_SUBST(HAVE_X11)
|
||||||
AM_CONDITIONAL([HAVE_X11], [test "x$HAVE_X11" = x1])
|
AM_CONDITIONAL([HAVE_X11], [test "x$HAVE_X11" = x1])
|
||||||
AS_IF([test "x$HAVE_X11" = "x1"], AC_DEFINE([HAVE_X11], 1, [Have X11?]))
|
AS_IF([test "x$HAVE_X11" = "x1"], AC_DEFINE([HAVE_X11], 1, [Have X11?]))
|
||||||
|
AS_IF([test "x$HAVE_XSETIOERROREXITHANDLER" = "xyes"], AC_DEFINE([HAVE_XSETIOERROREXITHANDLER], 1, [Have XSetIOErrorExitHandler function.]))
|
||||||
|
|
||||||
#### Capabilities (optional) ####
|
#### Capabilities (optional) ####
|
||||||
|
|
||||||
|
|
@ -1687,6 +1694,7 @@ echo "
|
||||||
|
|
||||||
Enable memfd shared memory: ${ENABLE_MEMFD}
|
Enable memfd shared memory: ${ENABLE_MEMFD}
|
||||||
Enable X11: ${ENABLE_X11}
|
Enable X11: ${ENABLE_X11}
|
||||||
|
Safe X11 I/O errors: ${HAVE_XSETIOERROREXITHANDLER}
|
||||||
Enable OSS Output: ${ENABLE_OSS_OUTPUT}
|
Enable OSS Output: ${ENABLE_OSS_OUTPUT}
|
||||||
Enable OSS Wrapper: ${ENABLE_OSS_WRAPPER}
|
Enable OSS Wrapper: ${ENABLE_OSS_WRAPPER}
|
||||||
Enable EsounD: ${ENABLE_ESOUND}
|
Enable EsounD: ${ENABLE_ESOUND}
|
||||||
|
|
|
||||||
|
|
@ -650,6 +650,9 @@ if x11_dep.found()
|
||||||
sm_dep = dependency('sm', required : true)
|
sm_dep = dependency('sm', required : true)
|
||||||
xtst_dep = dependency('xtst', required : true)
|
xtst_dep = dependency('xtst', required : true)
|
||||||
cdata.set('HAVE_X11', 1)
|
cdata.set('HAVE_X11', 1)
|
||||||
|
if cc.has_function('XSetIOErrorExitHandler', dependencies: x11_dep)
|
||||||
|
cdata.set('HAVE_XSETIOERROREXITHANDLER', 1)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Module dependencies
|
# Module dependencies
|
||||||
|
|
@ -847,6 +850,7 @@ summary = [
|
||||||
'',
|
'',
|
||||||
'Enable memfd shared memory: @0@'.format(cdata.has('HAVE_MEMFD')),
|
'Enable memfd shared memory: @0@'.format(cdata.has('HAVE_MEMFD')),
|
||||||
'Enable X11: @0@'.format(x11_dep.found()),
|
'Enable X11: @0@'.format(x11_dep.found()),
|
||||||
|
' Safe X11 I/O errors: @0@'.format(cdata.has('HAVE_XSETIOERROREXITHANDLER')),
|
||||||
'Enable OSS Output: @0@'.format(cdata.has('HAVE_OSS_OUTPUT')),
|
'Enable OSS Output: @0@'.format(cdata.has('HAVE_OSS_OUTPUT')),
|
||||||
'Enable OSS Wrapper: @0@'.format(cdata.has('HAVE_OSS_WRAPPER')),
|
'Enable OSS Wrapper: @0@'.format(cdata.has('HAVE_OSS_WRAPPER')),
|
||||||
# 'Enable EsounD: @0@'.format(${ENABLE_ESOUND}),
|
# 'Enable EsounD: @0@'.format(${ENABLE_ESOUND}),
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->x11_wrapper == w);
|
pa_assert(u->x11_wrapper == w);
|
||||||
|
|
||||||
|
pa_log_debug("X11 client kill callback called");
|
||||||
|
|
||||||
if (u->x11_client)
|
if (u->x11_client)
|
||||||
pa_x11_client_free(u->x11_client);
|
pa_x11_client_free(u->x11_client);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->x11_wrapper == w);
|
pa_assert(u->x11_wrapper == w);
|
||||||
|
|
||||||
|
pa_log_debug("X11 client kill callback called");
|
||||||
|
|
||||||
if (u->x11_client) {
|
if (u->x11_client) {
|
||||||
pa_x11_client_free(u->x11_client);
|
pa_x11_client_free(u->x11_client);
|
||||||
u->x11_client = NULL;
|
u->x11_client = NULL;
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,8 @@ static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->x11_wrapper == w);
|
pa_assert(u->x11_wrapper == w);
|
||||||
|
|
||||||
|
pa_log_debug("X11 client kill callback called");
|
||||||
|
|
||||||
if (u->x11_client)
|
if (u->x11_client)
|
||||||
pa_x11_client_free(u->x11_client);
|
pa_x11_client_free(u->x11_client);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ static void die_cb(SmcConn connection, SmPointer client_data) {
|
||||||
|
|
||||||
pa_log_debug("Got die message from XSMP.");
|
pa_log_debug("Got die message from XSMP.");
|
||||||
|
|
||||||
pa_x11_wrapper_kill(u->x11);
|
pa_x11_wrapper_kill_deferred(u->x11);
|
||||||
|
|
||||||
pa_x11_wrapper_unref(u->x11);
|
pa_x11_wrapper_unref(u->x11);
|
||||||
u->x11 = NULL;
|
u->x11 = NULL;
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@
|
||||||
|
|
||||||
#include "x11wrap.h"
|
#include "x11wrap.h"
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
typedef struct pa_x11_internal pa_x11_internal;
|
typedef struct pa_x11_internal pa_x11_internal;
|
||||||
|
|
||||||
struct pa_x11_internal {
|
struct pa_x11_internal {
|
||||||
|
|
@ -51,6 +53,7 @@ struct pa_x11_wrapper {
|
||||||
|
|
||||||
pa_defer_event* defer_event;
|
pa_defer_event* defer_event;
|
||||||
pa_io_event* io_event;
|
pa_io_event* io_event;
|
||||||
|
pa_defer_event* cleanup_event;
|
||||||
|
|
||||||
PA_LLIST_HEAD(pa_x11_client, clients);
|
PA_LLIST_HEAD(pa_x11_client, clients);
|
||||||
PA_LLIST_HEAD(pa_x11_internal, internals);
|
PA_LLIST_HEAD(pa_x11_internal, internals);
|
||||||
|
|
@ -64,6 +67,8 @@ struct pa_x11_client {
|
||||||
void *userdata;
|
void *userdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void x11_wrapper_kill(pa_x11_wrapper *w);
|
||||||
|
|
||||||
/* Dispatch all pending X11 events */
|
/* Dispatch all pending X11 events */
|
||||||
static void work(pa_x11_wrapper *w) {
|
static void work(pa_x11_wrapper *w) {
|
||||||
pa_assert(w);
|
pa_assert(w);
|
||||||
|
|
@ -167,6 +172,38 @@ static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening,
|
||||||
x11_internal_remove(w, (pa_x11_internal*) *watch_data);
|
x11_internal_remove(w, (pa_x11_internal*) *watch_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int x11_error_handler(Display* display, XErrorEvent* error_event) {
|
||||||
|
pa_log_warn("X11 error handler called");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int x11_io_error_handler(Display* display) {
|
||||||
|
pa_log_warn("X11 I/O error handler called");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deferred_x11_teardown(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
|
||||||
|
pa_x11_wrapper *w = userdata;
|
||||||
|
|
||||||
|
m->defer_enable(e, 0);
|
||||||
|
|
||||||
|
pa_log_debug("Start tearing down X11 modules after X11 I/O error");
|
||||||
|
|
||||||
|
x11_wrapper_kill(w);
|
||||||
|
|
||||||
|
pa_log_debug("Done tearing down X11 modules after X11 I/O error");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_XSETIOERROREXITHANDLER
|
||||||
|
static void x11_io_error_exit_handler(Display* display, void *userdata) {
|
||||||
|
pa_x11_wrapper *w = userdata;
|
||||||
|
|
||||||
|
pa_log_warn("X11 I/O error exit handler called, preparing to tear down X11 modules");
|
||||||
|
|
||||||
|
pa_x11_wrapper_kill_deferred(w);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
|
static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
|
||||||
pa_x11_wrapper*w;
|
pa_x11_wrapper*w;
|
||||||
Display *d;
|
Display *d;
|
||||||
|
|
@ -187,11 +224,20 @@ static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char
|
||||||
|
|
||||||
w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
|
w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
|
||||||
w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
|
w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
|
||||||
|
w->cleanup_event = c->mainloop->defer_new(c->mainloop, deferred_x11_teardown, w);
|
||||||
|
w->core->mainloop->defer_enable(w->cleanup_event, 0);
|
||||||
|
|
||||||
|
XSetErrorHandler(x11_error_handler);
|
||||||
|
XSetIOErrorHandler(x11_io_error_handler);
|
||||||
|
#ifdef HAVE_XSETIOERROREXITHANDLER
|
||||||
|
XSetIOErrorExitHandler(d, x11_io_error_exit_handler, w);
|
||||||
|
#endif
|
||||||
XAddConnectionWatch(d, x11_watch, (XPointer) w);
|
XAddConnectionWatch(d, x11_watch, (XPointer) w);
|
||||||
|
|
||||||
pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0);
|
pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0);
|
||||||
|
|
||||||
|
pa_log_debug("Created X11 connection wrapper '%s'", w->property_name);
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,9 +248,12 @@ static void x11_wrapper_free(pa_x11_wrapper*w) {
|
||||||
|
|
||||||
pa_assert(!w->clients);
|
pa_assert(!w->clients);
|
||||||
|
|
||||||
|
pa_log_debug("Destroying X11 connection wrapper '%s'", w->property_name);
|
||||||
|
|
||||||
XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
|
XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
|
||||||
XCloseDisplay(w->display);
|
XCloseDisplay(w->display);
|
||||||
|
|
||||||
|
w->core->mainloop->defer_free(w->cleanup_event);
|
||||||
w->core->mainloop->io_free(w->io_event);
|
w->core->mainloop->io_free(w->io_event);
|
||||||
w->core->mainloop->defer_free(w->defer_event);
|
w->core->mainloop->defer_free(w->defer_event);
|
||||||
|
|
||||||
|
|
@ -261,7 +310,15 @@ xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w) {
|
||||||
return XGetXCBConnection(pa_x11_wrapper_get_display(w));
|
return XGetXCBConnection(pa_x11_wrapper_get_display(w));
|
||||||
}
|
}
|
||||||
|
|
||||||
void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
|
void pa_x11_wrapper_kill_deferred(pa_x11_wrapper *w) {
|
||||||
|
pa_assert(w);
|
||||||
|
|
||||||
|
/* schedule X11 display teardown */
|
||||||
|
w->core->mainloop->defer_enable(w->cleanup_event, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kill the connection to the X11 display */
|
||||||
|
static void x11_wrapper_kill(pa_x11_wrapper *w) {
|
||||||
pa_x11_client *c, *n;
|
pa_x11_client *c, *n;
|
||||||
|
|
||||||
pa_assert(w);
|
pa_assert(w);
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
|
||||||
/* Return the XCB connection object for this connection */
|
/* Return the XCB connection object for this connection */
|
||||||
xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w);
|
xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w);
|
||||||
|
|
||||||
/* Kill the connection to the X11 display */
|
/* Initiate X11 connection teardown. */
|
||||||
void pa_x11_wrapper_kill(pa_x11_wrapper *w);
|
void pa_x11_wrapper_kill_deferred(pa_x11_wrapper *w);
|
||||||
|
|
||||||
/* Register an X11 client, that is called for each X11 event */
|
/* Register an X11 client, that is called for each X11 event */
|
||||||
pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
|
pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue