Skip to content

Commit 11cc491

Browse files
committed
Implement poll() on systems which support it properly.
This eliminates the restriction on maximum socket descriptor number.
1 parent 28211a4 commit 11cc491

File tree

3 files changed

+77
-2
lines changed

3 files changed

+77
-2
lines changed

src/compat.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,15 @@ typedef void* sockopt_arg_type;
9292
typedef char* sockopt_arg_type;
9393
#endif
9494

95+
// Note these both should work with the current usage of poll, but best to be safe
96+
// WIN32 poll is broken https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
97+
// __APPLE__ poll is broke https://github.com/bitcoin/bitcoin/pull/14336#issuecomment-437384408
98+
#if defined(__linux__)
99+
#define USE_POLL
100+
#endif
101+
95102
bool static inline IsSelectableSocket(const SOCKET& s) {
96-
#ifdef WIN32
103+
#if defined(USE_POLL) || defined(WIN32)
97104
return true;
98105
#else
99106
return (s < FD_SETSIZE);

src/net.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,18 @@
2626
#include <fcntl.h>
2727
#endif
2828

29+
#ifdef USE_POLL
30+
#include <poll.h>
31+
#endif
32+
2933
#ifdef USE_UPNP
3034
#include <miniupnpc/miniupnpc.h>
3135
#include <miniupnpc/miniwget.h>
3236
#include <miniupnpc/upnpcommands.h>
3337
#include <miniupnpc/upnperrors.h>
3438
#endif
3539

40+
#include <unordered_map>
3641

3742
#include <math.h>
3843

@@ -1307,6 +1312,49 @@ bool CConnman::GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &s
13071312
return !recv_set.empty() || !send_set.empty() || !error_set.empty();
13081313
}
13091314

1315+
#ifdef USE_POLL
1316+
void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set)
1317+
{
1318+
std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
1319+
if (!GenerateSelectSet(recv_select_set, send_select_set, error_select_set)) {
1320+
interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
1321+
return;
1322+
}
1323+
1324+
std::unordered_map<SOCKET, struct pollfd> pollfds;
1325+
for (SOCKET socket_id : recv_select_set) {
1326+
pollfds[socket_id].fd = socket_id;
1327+
pollfds[socket_id].events |= POLLIN;
1328+
}
1329+
1330+
for (SOCKET socket_id : send_select_set) {
1331+
pollfds[socket_id].fd = socket_id;
1332+
pollfds[socket_id].events |= POLLOUT;
1333+
}
1334+
1335+
for (SOCKET socket_id : error_select_set) {
1336+
pollfds[socket_id].fd = socket_id;
1337+
// These flags are ignored, but we set them for clarity
1338+
pollfds[socket_id].events |= POLLERR|POLLHUP;
1339+
}
1340+
1341+
std::vector<struct pollfd> vpollfds;
1342+
vpollfds.reserve(pollfds.size());
1343+
for (auto it : pollfds) {
1344+
vpollfds.push_back(std::move(it.second));
1345+
}
1346+
1347+
if (poll(vpollfds.data(), vpollfds.size(), SELECT_TIMEOUT_MILLISECONDS) < 0) return;
1348+
1349+
if (interruptNet) return;
1350+
1351+
for (struct pollfd pollfd_entry : vpollfds) {
1352+
if (pollfd_entry.revents & POLLIN) recv_set.insert(pollfd_entry.fd);
1353+
if (pollfd_entry.revents & POLLOUT) send_set.insert(pollfd_entry.fd);
1354+
if (pollfd_entry.revents & (POLLERR|POLLHUP)) error_set.insert(pollfd_entry.fd);
1355+
}
1356+
}
1357+
#else
13101358
void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set)
13111359
{
13121360
std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
@@ -1380,6 +1428,7 @@ void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_s
13801428
}
13811429
}
13821430
}
1431+
#endif
13831432

13841433
void CConnman::SocketHandler()
13851434
{

src/netbase.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
#include <codecvt>
2222
#endif
2323

24+
#ifdef USE_POLL
25+
#include <poll.h>
26+
#endif
27+
2428
#if !defined(MSG_NOSIGNAL)
2529
#define MSG_NOSIGNAL 0
2630
#endif
@@ -264,11 +268,19 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
264268
if (!IsSelectableSocket(hSocket)) {
265269
return IntrRecvError::NetworkError;
266270
}
267-
struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait));
271+
int timeout_ms = std::min(endTime - curTime, maxWait);
272+
#ifdef USE_POLL
273+
struct pollfd pollfd = {};
274+
pollfd.fd = hSocket;
275+
pollfd.events = POLLIN | POLLOUT;
276+
int nRet = poll(&pollfd, 1, timeout_ms);
277+
#else
278+
struct timeval tval = MillisToTimeval(timeout_ms);
268279
fd_set fdset;
269280
FD_ZERO(&fdset);
270281
FD_SET(hSocket, &fdset);
271282
int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval);
283+
#endif
272284
if (nRet == SOCKET_ERROR) {
273285
return IntrRecvError::NetworkError;
274286
}
@@ -499,11 +511,18 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
499511
// WSAEINVAL is here because some legacy version of winsock uses it
500512
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
501513
{
514+
#ifdef USE_POLL
515+
struct pollfd pollfd = {};
516+
pollfd.fd = hSocket;
517+
pollfd.events = POLLIN | POLLOUT;
518+
int nRet = poll(&pollfd, 1, nTimeout);
519+
#else
502520
struct timeval timeout = MillisToTimeval(nTimeout);
503521
fd_set fdset;
504522
FD_ZERO(&fdset);
505523
FD_SET(hSocket, &fdset);
506524
int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout);
525+
#endif
507526
if (nRet == 0)
508527
{
509528
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());

0 commit comments

Comments
 (0)