From 3bac2e5fb8ee6b02ad09e8e556edd270d7f4de9c Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Tue, 18 Apr 2023 16:39:00 +0100 Subject: event-loop: Handle EINTR and EAGAIN in wl_event_loop_dispatch This fixes an issue where it was not possible to start Gamescope under GDB on some setups. https://github.com/ValveSoftware/gamescope/issues/743 Any signals would cause epoll_wait to return -1 and set errno to EINTR. This also handles the EAGAIN case like the other polling loops in libwayland. Signed-off-by: Joshua Ashton --- src/event-loop.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/event-loop.c b/src/event-loop.c index 37cf95d..dd9a971 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -971,6 +971,57 @@ wl_event_loop_dispatch_idle(struct wl_event_loop *loop) } } +static int +timespec_to_ms(struct timespec value) +{ + return (value.tv_sec * 1000) + (value.tv_nsec / 1000000); +} + +static struct timespec +ms_to_timespec(int ms) +{ + struct timespec val; + val.tv_sec = ms / 1000; + val.tv_nsec = (ms % 1000) * 1000000; + return val; +} + +static struct timespec +timespec_normalize(struct timespec value) +{ + struct timespec result = value; + + while (result.tv_nsec >= 1000000000) { + result.tv_nsec -= 1000000000; + result.tv_sec++; + } + + while (result.tv_nsec < 0) { + result.tv_nsec += 1000000000; + result.tv_sec--; + } + + return result; +} + +static struct timespec +timespec_add(struct timespec a, struct timespec b) +{ + struct timespec result; + result.tv_sec = a.tv_sec + b.tv_sec; + result.tv_nsec = a.tv_nsec + b.tv_nsec; + return timespec_normalize(result); +} + +static struct timespec +timespec_sub(struct timespec a, struct timespec b) +{ + struct timespec result; + result.tv_sec = a.tv_sec - b.tv_sec; + result.tv_nsec = a.tv_nsec - b.tv_nsec; + return timespec_normalize(result); +} + /** Wait for events and dispatch them * * \param loop The event loop whose sources to wait for. @@ -998,10 +1049,34 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout) struct wl_event_source *source; int i, count; bool has_timers = false; + bool use_timeout = timeout > 0; + struct timespec now, end; wl_event_loop_dispatch_idle(loop); - count = epoll_wait(loop->epoll_fd, ep, ARRAY_LENGTH(ep), timeout); + if (use_timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + end = timespec_add(now, ms_to_timespec(timeout)); + } + + while (true) { + count = epoll_wait(loop->epoll_fd, ep, ARRAY_LENGTH(ep), timeout); + if (count >= 0) + break; /* have events or timeout */ + else if (count < 0 && errno != EINTR && errno != EAGAIN) + break; /* have error */ + + if (use_timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timeout = timespec_to_ms(timespec_sub(end, now)); + if (timeout <= 0) { + /* too late */ + count = 0; + break; + } + } + } + if (count < 0) return -1; -- cgit v1.2.1