From abc36d8f090dbae1b127f74a83b601d3fa3c8bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Jan 2020 21:10:08 +0100 Subject: [PATCH] wayland: wl_display_flush() never blocks Since it doesn't block, we need to detect EAGAIN failures and ensure we actually flush everything. If we don't, we sooner or later end up in a wayland client library call that aborts due to the socket buffer being full. Ideally, we'd simply enable POLLOUT in the FDM. However, we cannot write *anything* to the wayland socket until we've actually managed to send everything. This means enabling POLLOUT in the FDM wont work since we may (*will*) end up trying to write more data to it before we've flushed it. So, add a wrapper function, wayl_flush(), that acts as a blocking variant of wl_display_flush(), by detecting EAGAIN failiures and calling poll() itself, on the wayland socket only, until all data has been sent. --- render.c | 2 +- wayland.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- wayland.h | 1 + 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/render.c b/render.c index 4b012a90..25432110 100644 --- a/render.c +++ b/render.c @@ -867,7 +867,7 @@ grid_render(struct terminal *term) } wl_surface_commit(term->window->surface); - wl_display_flush(term->wl->display); + wayl_flush(term->wl); #if TIME_FRAME_RENDERING struct timeval end_time; diff --git a/wayland.c b/wayland.c index c0b305ca..6e4bb638 100644 --- a/wayland.c +++ b/wayland.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -573,7 +574,7 @@ fdm_wayl(struct fdm *fdm, int fd, int events, void *data) return false; } - wl_display_flush(wayl->display); + wayl_flush(wayl); return event_count != -1; } @@ -983,7 +984,7 @@ wayl_cursor_set(struct wayland *wayl, const struct terminal *term) wl_surface_set_buffer_scale(wayl->pointer.surface, scale); wl_surface_commit(wayl->pointer.surface); - wl_display_flush(wayl->display); + wayl_flush(wayl); return true; } @@ -1029,6 +1030,46 @@ wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface) return NULL; } +void +wayl_flush(struct wayland *wayl) +{ + while (true) { + int r = wl_display_flush(wayl->display); + if (r >= 0) + return; + + if (errno == EINTR) + continue; + + if (errno != EAGAIN) { + LOG_ERRNO("failed to flush wayland socket"); + return; + } + + struct pollfd fds[] = { + {.fd = wl_display_get_fd(wayl->display), .events = POLLOUT} + }; + + while (true) { + r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); + + if (r < 0) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + return; + } + + if (fds[0].revents & POLLHUP) + return; + + assert(fds[0].revents & POLLOUT); + break; + } + } +} + void wayl_roundtrip(struct wayland *wayl) { @@ -1037,5 +1078,5 @@ wayl_roundtrip(struct wayland *wayl) while (wl_display_prepare_read(wayl->display) != 0) wl_display_dispatch_pending(wayl->display); - wl_display_flush(wayl->display); + wayl_flush(wayl); } diff --git a/wayland.h b/wayland.h index eda9e009..ad352797 100644 --- a/wayland.h +++ b/wayland.h @@ -178,6 +178,7 @@ struct wayland { struct wayland *wayl_init(const struct config *conf, struct fdm *fdm); void wayl_destroy(struct wayland *wayl); +void wayl_flush(struct wayland *wayl); void wayl_roundtrip(struct wayland *wayl); struct terminal *wayl_terminal_from_surface(