Move XCB event handling into xcb_prepare_cb.

Previously, we used ev_check watchers, which are executed at the beginning of an
event loop iteration.

This was problematic if one of the handlers happened to fill the XCB event
queue, e.g. by reading a reply from X11 and an event happened in the meantime.

In that situation, we would hand control to the event loop, entirely ignoring
the pending event. This would manifest itself as a 1-minute hang,
reproducible (sometimes) in the i3 testsuite.

issue #2790 describes an instance of this issue in i3bar, and we fixed that by
changing the watcher priority to run last. Handling events in xcb_prepare_cb has
the same effect, as ev_prepare watchers are run just before the event loop goes
to sleep.
This commit is contained in:
Michael Stapelberg
2017-10-03 10:03:29 +02:00
parent 1946cc6cab
commit 0d8b6714e3
4 changed files with 31 additions and 64 deletions

View File

@ -39,7 +39,6 @@ static TAILQ_HEAD(state_head, placeholder_state) state_head =
static xcb_connection_t *restore_conn;
static struct ev_io *xcb_watcher;
static struct ev_check *xcb_check;
static struct ev_prepare *xcb_prepare;
static void restore_handle_event(int type, xcb_generic_event_t *event);
@ -49,10 +48,6 @@ static void restore_xcb_got_event(EV_P_ struct ev_io *w, int revents) {
}
static void restore_xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
xcb_flush(restore_conn);
}
static void restore_xcb_check_cb(EV_P_ ev_check *w, int revents) {
xcb_generic_event_t *event;
if (xcb_connection_has_error(restore_conn)) {
@ -77,6 +72,8 @@ static void restore_xcb_check_cb(EV_P_ ev_check *w, int revents) {
free(event);
}
xcb_flush(restore_conn);
}
/*
@ -91,7 +88,6 @@ void restore_connect(void) {
/* This is not the initial connect, but a reconnect, most likely
* because our X11 connection was killed (e.g. by a user with xkill. */
ev_io_stop(main_loop, xcb_watcher);
ev_check_stop(main_loop, xcb_check);
ev_prepare_stop(main_loop, xcb_prepare);
placeholder_state *state;
@ -107,7 +103,6 @@ void restore_connect(void) {
*/
xcb_disconnect(restore_conn);
free(xcb_watcher);
free(xcb_check);
free(xcb_prepare);
}
@ -124,15 +119,11 @@ void restore_connect(void) {
}
xcb_watcher = scalloc(1, sizeof(struct ev_io));
xcb_check = scalloc(1, sizeof(struct ev_check));
xcb_prepare = scalloc(1, sizeof(struct ev_prepare));
ev_io_init(xcb_watcher, restore_xcb_got_event, xcb_get_file_descriptor(restore_conn), EV_READ);
ev_io_start(main_loop, xcb_watcher);
ev_check_init(xcb_check, restore_xcb_check_cb);
ev_check_start(main_loop, xcb_check);
ev_prepare_init(xcb_prepare, restore_xcb_prepare_cb);
ev_prepare_start(main_loop, xcb_prepare);
}