From c4e4e0cef87c0d600fde63a336c74b49eca282db Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Sun, 17 Jul 2022 22:00:48 +0200 Subject: [PATCH] Cleanup epoll_create fallback Now allows the fallback on all platforms, if we doing one why not all? Also properly closes the file descriptor if the fcntl call fails. --- src/sys/unix/selector/epoll.rs | 72 ++++++++++++++++------------------ 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/sys/unix/selector/epoll.rs b/src/sys/unix/selector/epoll.rs index 477642a1..074d3a8e 100644 --- a/src/sys/unix/selector/epoll.rs +++ b/src/sys/unix/selector/epoll.rs @@ -23,7 +23,40 @@ pub struct Selector { impl Selector { pub fn new() -> io::Result { - let ep = new_epoll_fd()?; + #[cfg(not(target_os = "android"))] + let res = syscall!(epoll_create1(libc::O_CLOEXEC)); + + // On Android < API level 16 `epoll_create1` is not defined, so use a + // raw system call. + // According to libuv, `EPOLL_CLOEXEC` is not defined on Android API < + // 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform, + // so we use it instead. + #[cfg(target_os = "android")] + let res = syscall!(syscall(libc::SYS_epoll_create1, libc::EPOLL_CLOEXEC)); + + let ep = match res { + Ok(ep) => ep as RawFd, + Err(err) => { + // When `epoll_create1` is not available fall back to use + // `epoll_create` followed by `fcntl`. + if let Some(libc::ENOSYS) = err.raw_os_error() { + match syscall!(epoll_create(1024)) { + Ok(ep) => match syscall!(fcntl(ep, libc::F_SETFD, libc::FD_CLOEXEC)) { + Ok(ep) => ep as RawFd, + Err(err) => { + // `fcntl` failed, cleanup `ep`. + let _ = unsafe { libc::close(ep) }; + return Err(err); + } + }, + Err(err) => return Err(err), + } + } else { + return Err(err); + } + } + }; + Ok(Selector { #[cfg(debug_assertions)] id: NEXT_ID.fetch_add(1, Ordering::Relaxed), @@ -126,43 +159,6 @@ impl Drop for Selector { } } -/// Creates a new epoll file descriptor with close-on-exec flag set. -/// -/// close-on-exec is set atomically with `epoll_create1()` if possible, -/// otherwise `epoll_create()` and `fcntl()` calls are used as a fallback. -fn new_epoll_fd() -> io::Result { - // According to libuv, `EPOLL_CLOEXEC` is not defined on Android API < - // 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform, - // so we use it instead. - #[cfg(target_os = "android")] - let flag = libc::O_CLOEXEC; - #[cfg(not(target_os = "android"))] - let flag = libc::EPOLL_CLOEXEC; - - #[cfg(not(target_os = "android"))] - let ep = syscall!(epoll_create1(flag))?; - - // On Android try to use epoll_create1 syscall with an epoll_create fallback - // to support Android API level 16 which does not define epoll_create1 function. - #[cfg(target_os = "android")] - let ep = syscall!(syscall(libc::SYS_epoll_create1, flag)) - .map(|fd| fd as libc::c_int) - .or_else(|e| { - match e.raw_os_error() { - Some(libc::ENOSYS) => { - // Using epoll_create() followed by fcntl() instead of epoll_create1() with EPOLL_CLOEXEC - // flag for backwards compatibility. - let ep = syscall!(epoll_create(1024))?; - - syscall!(fcntl(ep, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| ep) - } - _ => Err(e), - } - })?; - - Ok(ep) -} - fn interests_to_epoll(interests: Interest) -> u32 { let mut kind = EPOLLET;