Use poll instead of select
Signed-off-by: Anthony Fieroni <bvbfan@abv.bg>
This commit is contained in:
parent
558f85e411
commit
e5c233691f
4 changed files with 92 additions and 9 deletions
|
@ -102,8 +102,15 @@ typedef void* sockopt_arg_type;
|
||||||
typedef char* sockopt_arg_type;
|
typedef char* sockopt_arg_type;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Note these both should work with the current usage of poll, but best to be safe
|
||||||
|
// WIN32 poll is broken https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
|
||||||
|
// __APPLE__ poll is broke https://github.com/bitcoin/bitcoin/pull/14336#issuecomment-437384408
|
||||||
|
#if defined(__linux__)
|
||||||
|
#define USE_POLL
|
||||||
|
#endif
|
||||||
|
|
||||||
bool static inline IsSelectableSocket(const SOCKET& s) {
|
bool static inline IsSelectableSocket(const SOCKET& s) {
|
||||||
#ifdef WIN32
|
#if defined(USE_POLL) || defined(WIN32)
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
return (s < FD_SETSIZE);
|
return (s < FD_SETSIZE);
|
||||||
|
|
|
@ -941,8 +941,13 @@ bool AppInitParameterInteraction()
|
||||||
|
|
||||||
// Trim requested connection counts, to fit into system limitations
|
// Trim requested connection counts, to fit into system limitations
|
||||||
// <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
|
// <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
|
||||||
nMaxConnections = std::max(std::min<int>(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
|
|
||||||
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS);
|
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS);
|
||||||
|
#ifdef USE_POLL
|
||||||
|
int fd_max = nFD;
|
||||||
|
#else
|
||||||
|
int fd_max = FD_SETSIZE;
|
||||||
|
#endif
|
||||||
|
nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
|
||||||
if (nFD < MIN_CORE_FILEDESCRIPTORS)
|
if (nFD < MIN_CORE_FILEDESCRIPTORS)
|
||||||
return InitError(_("Not enough file descriptors available."));
|
return InitError(_("Not enough file descriptors available."));
|
||||||
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
|
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
|
||||||
|
|
62
src/net.cpp
62
src/net.cpp
|
@ -26,6 +26,10 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
#include <poll.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_UPNP
|
#ifdef USE_UPNP
|
||||||
#include <miniupnpc/miniupnpc.h>
|
#include <miniupnpc/miniupnpc.h>
|
||||||
#include <miniupnpc/miniwget.h>
|
#include <miniupnpc/miniwget.h>
|
||||||
|
@ -35,6 +39,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
|
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
|
||||||
#define DUMP_ADDRESSES_INTERVAL 900
|
#define DUMP_ADDRESSES_INTERVAL 900
|
||||||
|
@ -71,6 +76,10 @@ enum BindFlags {
|
||||||
BF_WHITELIST = (1U << 2),
|
BF_WHITELIST = (1U << 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The set of sockets cannot be modified while waiting
|
||||||
|
// The sleep time needs to be small to avoid new sockets stalling
|
||||||
|
static const uint64_t SELECT_TIMEOUT_MILLISECONDS = 50;
|
||||||
|
|
||||||
const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
|
const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
|
||||||
|
|
||||||
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
||||||
|
@ -1232,23 +1241,33 @@ void CConnman::ThreadSocketHandler()
|
||||||
//
|
//
|
||||||
// Find which sockets have data to receive
|
// Find which sockets have data to receive
|
||||||
//
|
//
|
||||||
struct timeval timeout;
|
|
||||||
timeout.tv_sec = 0;
|
|
||||||
timeout.tv_usec = 50000; // frequency to poll pnode->vSend
|
|
||||||
|
|
||||||
fd_set fdsetRecv;
|
fd_set fdsetRecv;
|
||||||
fd_set fdsetSend;
|
fd_set fdsetSend;
|
||||||
fd_set fdsetError;
|
fd_set fdsetError;
|
||||||
FD_ZERO(&fdsetRecv);
|
FD_ZERO(&fdsetRecv);
|
||||||
FD_ZERO(&fdsetSend);
|
FD_ZERO(&fdsetSend);
|
||||||
FD_ZERO(&fdsetError);
|
FD_ZERO(&fdsetError);
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
std::unordered_map<SOCKET, struct pollfd> pollfds;
|
||||||
|
#else
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = SELECT_TIMEOUT_MILLISECONDS * 1000; // frequency to poll pnode->vSend
|
||||||
|
|
||||||
SOCKET hSocketMax = 0;
|
SOCKET hSocketMax = 0;
|
||||||
bool have_fds = false;
|
bool have_fds = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
for (const ListenSocket& hListenSocket : vhListenSocket) {
|
for (const ListenSocket& hListenSocket : vhListenSocket) {
|
||||||
|
#ifdef USE_POLL
|
||||||
|
pollfds[hListenSocket.socket].fd = hListenSocket.socket;
|
||||||
|
pollfds[hListenSocket.socket].events |= POLLIN;
|
||||||
|
#else
|
||||||
FD_SET(hListenSocket.socket, &fdsetRecv);
|
FD_SET(hListenSocket.socket, &fdsetRecv);
|
||||||
hSocketMax = std::max(hSocketMax, hListenSocket.socket);
|
hSocketMax = std::max(hSocketMax, hListenSocket.socket);
|
||||||
have_fds = true;
|
have_fds = true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1277,24 +1296,52 @@ void CConnman::ThreadSocketHandler()
|
||||||
if (pnode->hSocket == INVALID_SOCKET)
|
if (pnode->hSocket == INVALID_SOCKET)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
pollfds[pnode->hSocket].fd = pnode->hSocket;
|
||||||
|
pollfds[pnode->hSocket].events |= POLLERR|POLLHUP;
|
||||||
|
#else
|
||||||
FD_SET(pnode->hSocket, &fdsetError);
|
FD_SET(pnode->hSocket, &fdsetError);
|
||||||
hSocketMax = std::max(hSocketMax, pnode->hSocket);
|
hSocketMax = std::max(hSocketMax, pnode->hSocket);
|
||||||
have_fds = true;
|
have_fds = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (select_send) {
|
if (select_send) {
|
||||||
|
#ifdef USE_POLL
|
||||||
|
pollfds[pnode->hSocket].fd = pnode->hSocket;
|
||||||
|
pollfds[pnode->hSocket].events |= POLLOUT;
|
||||||
|
#else
|
||||||
FD_SET(pnode->hSocket, &fdsetSend);
|
FD_SET(pnode->hSocket, &fdsetSend);
|
||||||
|
#endif
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (select_recv) {
|
if (select_recv) {
|
||||||
|
#ifdef USE_POLL
|
||||||
|
pollfds[pnode->hSocket].fd = pnode->hSocket;
|
||||||
|
pollfds[pnode->hSocket].events |= POLLIN;
|
||||||
|
#else
|
||||||
FD_SET(pnode->hSocket, &fdsetRecv);
|
FD_SET(pnode->hSocket, &fdsetRecv);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
std::vector<struct pollfd> vpollfds;
|
||||||
|
vpollfds.reserve(pollfds.size());
|
||||||
|
for (auto it : pollfds)
|
||||||
|
vpollfds.push_back(std::move(it.second));
|
||||||
|
|
||||||
|
if (poll(vpollfds.data(), vpollfds.size(), SELECT_TIMEOUT_MILLISECONDS) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (struct pollfd pollfd_entry : vpollfds) {
|
||||||
|
if (pollfd_entry.revents & POLLIN) FD_SET(pollfd_entry.fd, &fdsetRecv);
|
||||||
|
if (pollfd_entry.revents & POLLOUT) FD_SET(pollfd_entry.fd, &fdsetSend);
|
||||||
|
if (pollfd_entry.revents & (POLLERR|POLLHUP)) FD_SET(pollfd_entry.fd, &fdsetError);
|
||||||
|
}
|
||||||
|
#else
|
||||||
int nSelect = select(have_fds ? hSocketMax + 1 : 0,
|
int nSelect = select(have_fds ? hSocketMax + 1 : 0,
|
||||||
&fdsetRecv, &fdsetSend, &fdsetError, &timeout);
|
&fdsetRecv, &fdsetSend, &fdsetError, &timeout);
|
||||||
if (interruptNet)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (nSelect == SOCKET_ERROR)
|
if (nSelect == SOCKET_ERROR)
|
||||||
{
|
{
|
||||||
|
@ -1310,7 +1357,10 @@ void CConnman::ThreadSocketHandler()
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000)))
|
if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000)))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (interruptNet)
|
||||||
|
return;
|
||||||
//
|
//
|
||||||
// Accept new connections
|
// Accept new connections
|
||||||
//
|
//
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
#include <poll.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
|
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
|
||||||
|
|
||||||
#if !defined(MSG_NOSIGNAL)
|
#if !defined(MSG_NOSIGNAL)
|
||||||
|
@ -264,11 +268,21 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
|
||||||
if (!IsSelectableSocket(hSocket)) {
|
if (!IsSelectableSocket(hSocket)) {
|
||||||
return IntrRecvError::NetworkError;
|
return IntrRecvError::NetworkError;
|
||||||
}
|
}
|
||||||
struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait));
|
// Only wait at most maxWait milliseconds at a time, unless
|
||||||
|
// we're approaching the end of the specified total timeout
|
||||||
|
int timeout_ms = std::min(endTime - curTime, maxWait);
|
||||||
|
#ifdef USE_POLL
|
||||||
|
struct pollfd pollfd = {};
|
||||||
|
pollfd.fd = hSocket;
|
||||||
|
pollfd.events = POLLIN;
|
||||||
|
int nRet = poll(&pollfd, 1, timeout_ms);
|
||||||
|
#else
|
||||||
|
struct timeval tval = MillisToTimeval(timeout_ms);
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
FD_ZERO(&fdset);
|
FD_ZERO(&fdset);
|
||||||
FD_SET(hSocket, &fdset);
|
FD_SET(hSocket, &fdset);
|
||||||
int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval);
|
int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval);
|
||||||
|
#endif
|
||||||
if (nRet == SOCKET_ERROR) {
|
if (nRet == SOCKET_ERROR) {
|
||||||
return IntrRecvError::NetworkError;
|
return IntrRecvError::NetworkError;
|
||||||
}
|
}
|
||||||
|
@ -499,11 +513,18 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
|
||||||
// WSAEINVAL is here because some legacy version of winsock uses it
|
// WSAEINVAL is here because some legacy version of winsock uses it
|
||||||
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_POLL
|
||||||
|
struct pollfd pollfd = {};
|
||||||
|
pollfd.fd = hSocket;
|
||||||
|
pollfd.events = POLLIN | POLLOUT;
|
||||||
|
int nRet = poll(&pollfd, 1, nTimeout);
|
||||||
|
#else
|
||||||
struct timeval timeout = MillisToTimeval(nTimeout);
|
struct timeval timeout = MillisToTimeval(nTimeout);
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
FD_ZERO(&fdset);
|
FD_ZERO(&fdset);
|
||||||
FD_SET(hSocket, &fdset);
|
FD_SET(hSocket, &fdset);
|
||||||
int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout);
|
int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout);
|
||||||
|
#endif
|
||||||
if (nRet == 0)
|
if (nRet == 0)
|
||||||
{
|
{
|
||||||
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());
|
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());
|
||||||
|
|
Loading…
Reference in a new issue