Skip to content

Commit

Permalink
Improve polling for available file descriptors
Browse files Browse the repository at this point in the history
 - Remove 8MB of memory for each socket created
 - Reduce available file descriptor polling to minimum necessary
   - roughly a 1000 times faster
   - old duration: 34'571'539 ns
   - new duration: 34'464 ns
  • Loading branch information
ReeRichard committed Feb 13, 2024
1 parent 845f746 commit f09e5d0
Showing 1 changed file with 27 additions and 35 deletions.
62 changes: 27 additions & 35 deletions utilities/xmlrpcpp/src/XmlRpcServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,6 @@ XmlRpcServer::XmlRpcServer()
_accept_error(false),
_accept_retry_time_sec(0.0)
{
#if !defined(_WINDOWS)
struct rlimit limit = { .rlim_cur = 0, .rlim_max = 0 };
unsigned int max_files = 1024;

if(getrlimit(RLIMIT_NOFILE, &limit) == 0) {
max_files = limit.rlim_max;
if( limit.rlim_max == RLIM_INFINITY ) {
max_files = 0;
}
} else {
XmlRpcUtil::error("Could not get open file limit: %s", strerror(errno));
}
pollfds.resize(max_files);
for(unsigned int i=0; i<max_files; i++) {
// Set up file descriptor query for all events.
pollfds[i].fd = i;
pollfds[i].events = POLLIN | POLLPRI | POLLOUT;
}
#endif

// Ask dispatch not to close this socket if it becomes unreadable.
setKeepOpen(true);
}
Expand Down Expand Up @@ -233,14 +213,33 @@ bool XmlRpcServer::enoughFreeFDs() {
if( limit.rlim_max == RLIM_INFINITY ) {
return true;
}
} else {
// The man page for getrlimit says that it can fail if the requested
// resource is invalid or the second argument is invalid. I'm not sure
// either of these can actually fail in this code, but it's better to
// check.
XmlRpcUtil::error("XmlRpcServer::enoughFreeFDs: Could not get open file "
"limit, getrlimit() failed: %s", strerror(errno));
return false;
}

// Poll the available file descriptors.
// The POSIX specification guarantees that rlim_cur will always be less or
// equal to the process's initial rlim_max, so we don't need an additional
// bounds check here.
if(poll(&pollfds[0], limit.rlim_cur, 1) >= 0) {
for(rlim_t i=0; i<limit.rlim_cur; i++) {
if(pollfds[i].revents & POLLNVAL) {
// List of all file descriptors, used for counting open files.
std::vector<struct pollfd> poll_fds;
poll_fds.resize(limit.rlim_cur > FREE_FD_BUFFER ? FREE_FD_BUFFER : limit.rlim_cur);

// Poll the available file descriptors.
// The POSIX specification guarantees that rlim_cur will always be less or
// equal to the process's initial rlim_max, so we don't need an additional
// bounds check here.
for(unsigned long long offset=0; offset<limit.rlim_cur; offset += FREE_FD_BUFFER) {
for(unsigned int i=0; i<poll_fds.size(); i++) {
// Set up file descriptor query for all events.
poll_fds[i].fd = i + offset;
poll_fds[i].events = POLLIN | POLLPRI | POLLOUT;
}
if(poll(&poll_fds[0], poll_fds.size(), 1) >= 0) {
for(rlim_t i=0; i<poll_fds.size(); i++) {
if(poll_fds[i].revents & POLLNVAL) {
free_fds++;
}
if (free_fds >= FREE_FD_BUFFER) {
Expand All @@ -249,18 +248,11 @@ bool XmlRpcServer::enoughFreeFDs() {
}
}
} else {
// poll() may fail if interrupted, if the pollfds array is a bad pointer,
// poll() may fail if interrupted, if the poll_fds array is a bad pointer,
// if nfds exceeds RLIMIT_NOFILE, or if the system is out of memory.
XmlRpcUtil::error("XmlRpcServer::enoughFreeFDs: poll() failed: %s",
strerror(errno));
}
} else {
// The man page for getrlimit says that it can fail if the requested
// resource is invalid or the second argument is invalid. I'm not sure
// either of these can actually fail in this code, but it's better to
// check.
XmlRpcUtil::error("XmlRpcServer::enoughFreeFDs: Could not get open file "
"limit, getrlimit() failed: %s", strerror(errno));
}

return false;
Expand Down

0 comments on commit f09e5d0

Please sign in to comment.