From: William Horvath william@horvath.blog
This fixes a real problem and improves behavior at the same time. - Fixes: hanging when trying to run Wine built with epoll_pwait2 headers available but epoll_pwait2 is missing in the run-time kernel (i.e. build 5.11->run 5.10)
- Improvement: bring the improved timeout resolution to Wine builds which didn't have epoll_pwait2 at compile time, if the run-time kernel supports it (i.e. build 5.10->run 6.14)
This last point is especially important in my opinion, as it applies to official Proton builds. Proton is (currently) built in Debian 11 with kernel 5.10, but SteamOS is a moving target with a much newer kernel version being used to run these Wine builds.
These builds use the lower resolution timeouts if epoll_pwait2 is only checked for at run-time, when it can cheaply and easily be done at run-time instead.
Fixes: 87ca5db40e2c1b37423bdc25101a5c5e39e67e6f --- configure.ac | 1 - server/fd.c | 23 ++++++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/configure.ac b/configure.ac index 9e32070f610..05157e80d55 100644 --- a/configure.ac +++ b/configure.ac @@ -2072,7 +2072,6 @@ AC_CHECK_FUNCS(\ dladdr1 \ dlinfo \ epoll_create \ - epoll_pwait2 \ fstatfs \ futimens \ futimes \ diff --git a/server/fd.c b/server/fd.c index e1b969a0439..640f9e2a79d 100644 --- a/server/fd.c +++ b/server/fd.c @@ -100,6 +100,7 @@ #include "ddk/wdm.h"
#if defined(HAVE_SYS_EPOLL_H) && defined(HAVE_EPOLL_CREATE) +# include <dlfcn.h> # include <sys/epoll.h> # define USE_EPOLL #endif /* HAVE_SYS_EPOLL_H && HAVE_EPOLL_CREATE */ @@ -561,6 +562,20 @@ static inline void remove_epoll_user( struct fd *fd, int user ) } }
+static int (*pepoll_pwait2)( int, struct epoll_event *, int, const struct timespec *, const sigset_t * ); + +/* dispatch to the highest resolution epoll available */ +static inline int do_epoll_wait( struct epoll_event *events, int maxevents, const struct timespec *timeout_ts, int timeout_ms ) +{ + if (pepoll_pwait2) + { + int ret = pepoll_pwait2( epoll_fd, events, maxevents, timeout_ts, NULL ); + if (ret == -1 && errno == ENOSYS) pepoll_pwait2 = NULL; /* broken, fall back to epoll_wait */ + else return ret; + } + return epoll_wait( epoll_fd, events, maxevents, timeout_ms ); +} + static inline void main_loop_epoll(void) { int i, ret, timeout; @@ -574,6 +589,8 @@ static inline void main_loop_epoll(void)
if (epoll_fd == -1) return;
+ pepoll_pwait2 = dlsym( RTLD_DEFAULT, "epoll_pwait2" ); + while (active_users) { timeout = get_next_timeout( &ts ); @@ -581,11 +598,7 @@ static inline void main_loop_epoll(void) if (!active_users) break; /* last user removed by a timeout */ if (epoll_fd == -1) break; /* an error occurred with epoll */
-#ifdef HAVE_EPOLL_PWAIT2 - ret = epoll_pwait2( epoll_fd, events, ARRAY_SIZE( events ), timeout == -1 ? NULL : &ts, NULL ); -#else - ret = epoll_wait( epoll_fd, events, ARRAY_SIZE( events ), timeout ); -#endif + ret = do_epoll_wait( events, ARRAY_SIZE( events ), timeout == -1 ? NULL : &ts, timeout );
set_current_time();