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.
This commit is contained in:
Daniel Eklöf 2020-01-04 21:10:08 +01:00
parent 2128d5912f
commit abc36d8f09
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
3 changed files with 46 additions and 4 deletions

View file

@ -867,7 +867,7 @@ grid_render(struct terminal *term)
} }
wl_surface_commit(term->window->surface); wl_surface_commit(term->window->surface);
wl_display_flush(term->wl->display); wayl_flush(term->wl);
#if TIME_FRAME_RENDERING #if TIME_FRAME_RENDERING
struct timeval end_time; struct timeval end_time;

View file

@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <poll.h>
#include <sys/timerfd.h> #include <sys/timerfd.h>
#include <sys/epoll.h> #include <sys/epoll.h>
@ -573,7 +574,7 @@ fdm_wayl(struct fdm *fdm, int fd, int events, void *data)
return false; return false;
} }
wl_display_flush(wayl->display); wayl_flush(wayl);
return event_count != -1; 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_set_buffer_scale(wayl->pointer.surface, scale);
wl_surface_commit(wayl->pointer.surface); wl_surface_commit(wayl->pointer.surface);
wl_display_flush(wayl->display); wayl_flush(wayl);
return true; return true;
} }
@ -1029,6 +1030,46 @@ wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface)
return NULL; 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 void
wayl_roundtrip(struct wayland *wayl) wayl_roundtrip(struct wayland *wayl)
{ {
@ -1037,5 +1078,5 @@ wayl_roundtrip(struct wayland *wayl)
while (wl_display_prepare_read(wayl->display) != 0) while (wl_display_prepare_read(wayl->display) != 0)
wl_display_dispatch_pending(wayl->display); wl_display_dispatch_pending(wayl->display);
wl_display_flush(wayl->display); wayl_flush(wayl);
} }

View file

@ -178,6 +178,7 @@ struct wayland {
struct wayland *wayl_init(const struct config *conf, struct fdm *fdm); struct wayland *wayl_init(const struct config *conf, struct fdm *fdm);
void wayl_destroy(struct wayland *wayl); void wayl_destroy(struct wayland *wayl);
void wayl_flush(struct wayland *wayl);
void wayl_roundtrip(struct wayland *wayl); void wayl_roundtrip(struct wayland *wayl);
struct terminal *wayl_terminal_from_surface( struct terminal *wayl_terminal_from_surface(