From 9188ac60eb8884f273c41849d205bb0df2a12e48 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 21 Feb 2019 22:12:47 +0100 Subject: [PATCH] MINOR: fd: implement an optimised my_closefrom() function The idea is that poll() can set the POLLNVAL flag for each invalid FD in a pollfd list. Thus this function makes use of poll() when compiled in, and builds lists of up to 1024 FDs at once, checks the output and only closes those which do not have this flag set. Tests show that this is about twice as fast as blindly calling close() for each closed fd. --- src/fd.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/fd.c b/src/fd.c index 92d093cb63..33b8900f3b 100644 --- a/src/fd.c +++ b/src/fd.c @@ -151,6 +151,11 @@ #include #include +#if defined(ENABLE_POLL) +#include +#include +#endif + #include #include @@ -461,6 +466,62 @@ void fd_process_cached_events() fdlist_process_cached_events(&fd_cache); } +#if defined(ENABLE_POLL) +/* This is a portable implementation of closefrom(). It closes all open file + * descriptors starting at and above. It relies on the fact that poll() + * will return POLLNVAL for each invalid (hence close) file descriptor passed + * in argument in order to skip them. It acts with batches of FDs and will + * typically perform one poll() call per 1024 FDs so the overhead is low in + * case all FDs have to be closed. + */ +void my_closefrom(int start) +{ + struct pollfd poll_events[1024]; + struct rlimit limit; + int nbfds, fd, ret, idx; + int step, next; + + if (getrlimit(RLIMIT_NOFILE, &limit) == 0) + step = nbfds = limit.rlim_cur; + else + step = nbfds = 0; + + if (nbfds <= 0) { + /* set safe limit */ + nbfds = 1024; + step = 256; + } + + if (step > sizeof(poll_events) / sizeof(poll_events[0])) + step = sizeof(poll_events) / sizeof(poll_events[0]); + + while (start < nbfds) { + next = (start / step + 1) * step; + + for (fd = start; fd < next && fd < nbfds; fd++) { + poll_events[fd - start].fd = fd; + poll_events[fd - start].events = 0; + } + + do { + ret = poll(poll_events, fd - start, 0); + if (ret >= 0) + break; + } while (errno == EAGAIN || errno == EINTR || errno == ENOMEM); + + for (idx = 0; idx < ret; idx++) { + if (poll_events[idx].revents & POLLNVAL) + continue; /* already closed */ + + fd = poll_events[idx].fd; + close(fd); + } + start = next; + } +} + +#else // defined(ENABLE_POLL) + /* This is a portable implementation of closefrom(). It closes all open file * descriptors starting at and above. This is a naive version for use * when the operating system provides no alternative. @@ -481,6 +542,7 @@ void my_closefrom(int start) while (start < nbfds) close(start++); } +#endif // defined(ENABLE_POLL) /* disable the specified poller */ void disable_poller(const char *poller_name) -- 2.39.5