From: Michel Dänzer Date: Fri, 5 Jun 2020 17:07:55 +0000 (+0200) Subject: loader/dri3: Check for window destruction in dri3_wait_for_event_locked X-Git-Url: https://git.libre-soc.org/?p=mesa.git;a=commitdiff_plain;h=d7d7687829875e401690219d4a72458fb2bbe4de loader/dri3: Check for window destruction in dri3_wait_for_event_locked If the underlying X11 window gets destroyed, the event we're waiting for may never be delivered, in which case xcb_wait_for_special_event would hang indefinitely. Solution: 1. Use xcb_poll_for_special_event to check if an event has arrived yet. 2. If not, Wait up to ~1s for XCB's file descriptor to become readable; if it does, go back to step 1. 3. If the file descriptor didn't become readable, make a round-trip to the X server to check that the window still exists. Go back to step 1 if it does, otherwise bail. Also add an early bail-out when it's known that the window was destroyed. Cc: mesa-stable Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/116 Reviewed-by: Kenneth Graunke Part-of: --- diff --git a/src/loader/loader_dri3_helper.c b/src/loader/loader_dri3_helper.c index 425faba8f64..97b5182994a 100644 --- a/src/loader/loader_dri3_helper.c +++ b/src/loader/loader_dri3_helper.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -532,20 +533,55 @@ dri3_wait_for_event_locked(struct loader_dri3_drawable *draw, xcb_generic_event_t *ev; xcb_present_generic_event_t *ge; + if (draw->window_destroyed) + return false; + xcb_flush(draw->conn); /* Only have one thread waiting for events at a time */ if (draw->has_event_waiter) { cnd_wait(&draw->event_cnd, &draw->mtx); + if (draw->window_destroyed) + return false; if (full_sequence) *full_sequence = draw->last_special_event_sequence; /* Another thread has updated the protected info, so retest. */ return true; } else { + struct pollfd pfds; + draw->has_event_waiter = true; /* Allow other threads access to the drawable while we're waiting. */ mtx_unlock(&draw->mtx); - ev = xcb_wait_for_special_event(draw->conn, draw->special_event); + + pfds.fd = xcb_get_file_descriptor(draw->conn); + pfds.events = POLLIN; + + ev = xcb_poll_for_special_event(draw->conn, draw->special_event); + while (!ev) { + /* Wait up to ~1s for the XCB FD to become readable */ + if (poll(&pfds, 1, 1000) < 1) { + xcb_get_window_attributes_cookie_t cookie; + xcb_get_window_attributes_reply_t *attrib; + xcb_generic_error_t *error; + + /* Check if the window still exists */ + cookie = xcb_get_window_attributes(draw->conn, draw->drawable); + attrib = xcb_get_window_attributes_reply(draw->conn, cookie, &error); + free(attrib); + + if (error) { + if (error->error_code == BadWindow) + draw->window_destroyed = true; + + free(error); + break; + } + } + + ev = xcb_poll_for_special_event(draw->conn, draw->special_event); + } + mtx_lock(&draw->mtx); draw->has_event_waiter = false; cnd_broadcast(&draw->event_cnd); diff --git a/src/loader/loader_dri3_helper.h b/src/loader/loader_dri3_helper.h index c314e4c5a9d..0961534a792 100644 --- a/src/loader/loader_dri3_helper.h +++ b/src/loader/loader_dri3_helper.h @@ -122,6 +122,7 @@ struct loader_dri3_drawable { uint8_t have_back; uint8_t have_fake_front; uint8_t is_pixmap; + bool window_destroyed; /* Information about the GPU owning the buffer */ __DRIscreen *dri_screen;