From 57a20366a30588ee9a2e7bab61a966872d0d24ba Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Mon, 3 Feb 2025 15:53:54 +0100 Subject: [PATCH] Refactor detail::select_read() for up to two FDs Add an overload for detail::select_read() that accepts two FDs and reports readability via two boolean out parameters. This is required to implement interruptible reads. Both overloads build on top of a common implementation that optimizes to the same assembly for the common, single FD case. --- httplib.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/httplib.h b/httplib.h index 1fec2fe..28e95c1 100644 --- a/httplib.h +++ b/httplib.h @@ -3184,34 +3184,75 @@ inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size, }); } -inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +template +inline ssize_t select_read_impl(socket_t sock, socket_t extra_fd, time_t sec, + time_t usec, bool *sock_readable, + bool *extra_fd_readable) { #ifdef CPPHTTPLIB_USE_POLL - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLIN; + constexpr size_t nfds = WithExtraFD ? 2 : 1; + struct pollfd pfd_read[nfds]; + pfd_read[0].fd = sock; + pfd_read[0].events = POLLIN; + if (WithExtraFD) { + pfd_read[1].fd = extra_fd; + pfd_read[1].events = POLLIN; + } auto timeout = static_cast(sec * 1000 + usec / 1000); - return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + size_t ret = handle_EINTR([&]() { return poll(pfd_read, nfds, timeout); }); + if (WithExtraFD && ret > 0) { + assert(sock_readable && extra_fd_readable); + *sock_readable = pfd_read[0].revents & POLLIN; + *extra_fd_readable = pfd_read[1].revents & POLLIN; + } + return ret; #else #ifndef _WIN32 - if (sock >= FD_SETSIZE) { return -1; } + if (sock >= FD_SETSIZE || (WithExtraFD && extra_fd >= FD_SETSIZE)) { + return -1; + } #endif + int nfds; fd_set fds; FD_ZERO(&fds); FD_SET(sock, &fds); + if (WithExtraFD) { + FD_SET(extra_fd, &fds); + nfds = static_cast((std::max)(sock, extra_fd) + 1); + } else { + nfds = static_cast(sock + 1); + } + timeval tv; tv.tv_sec = static_cast(sec); tv.tv_usec = static_cast(usec); - return handle_EINTR([&]() { - return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); - }); + ssize_t ret = + handle_EINTR([&]() { return select(nfds, &fds, nullptr, nullptr, &tv); }); + if (WithExtraFD && ret > 0) { + assert(sock_readable && extra_fd_readable); + *sock_readable = FD_ISSET(sock, &fds); + *extra_fd_readable = FD_ISSET(extra_fd, &fds); + } + return ret; #endif } +inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { + return select_read_impl(sock, INVALID_SOCKET, sec, usec, nullptr, + nullptr); +} + +inline ssize_t select_read(socket_t sock, socket_t extra_fd, time_t sec, + time_t usec, bool &sock_readable, + bool &extra_fd_readable) { + return select_read_impl(sock, extra_fd, sec, usec, &sock_readable, + &extra_fd_readable); +} + inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { #ifdef CPPHTTPLIB_USE_POLL struct pollfd pfd_read;