diff options
author | Joshua Ashton <joshua@froggi.es> | 2023-04-18 16:39:00 +0100 |
---|---|---|
committer | Daniel Stone <daniels@collabora.com> | 2023-05-02 12:36:35 +0100 |
commit | 3bac2e5fb8ee6b02ad09e8e556edd270d7f4de9c (patch) | |
tree | 0fbf3a8cc8826af495c405a293d294f7b72fc346 /src | |
parent | 1e259a255a07cd16b31fc99e6bba3e9368fae6d9 (diff) | |
download | wayland-3bac2e5fb8ee6b02ad09e8e556edd270d7f4de9c.tar.gz |
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 <joshua@froggi.es>
Diffstat (limited to 'src')
-rw-r--r-- | src/event-loop.c | 77 |
1 files changed, 76 insertions, 1 deletions
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; |