upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/makefile.mingw7
-rw-r--r--src/net.cpp152
-rw-r--r--src/net.h15
3 files changed, 92 insertions, 82 deletions
diff --git a/src/makefile.mingw b/src/makefile.mingw
index b946e50..678d2e8 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -14,13 +14,8 @@ WARNFLAGS = -Wformat -Werror=format \
14 -Wno-deprecated-declarations -Wno-write-strings \ 14 -Wno-deprecated-declarations -Wno-write-strings \
15 -Wno-narrowing -Wno-unused-result 15 -Wno-narrowing -Wno-unused-result
16 16
17# FD_SETSIZE has to be defined before winsock2.h and it has to be the same
18# number in every translation unit -- fd_set carries an array of that length,
19# so disagreeing about it means disagreeing about sizeof(fd_set). That is why
20# it lives here as a -D and not as a #define in a header. Windows defaults to
21# 64, which capped this node at 63 watchable peers no matter how many it had.
22CXXFLAGS = -std=gnu++14 -O2 $(WARNFLAGS) -fpermissive -pthread \ 17CXXFLAGS = -std=gnu++14 -O2 $(WARNFLAGS) -fpermissive -pthread \
23 -DWIN32 -DNOPCH -DFD_SETSIZE=1024 \ 18 -DWIN32 -DNOPCH \
24 -DSECP256K1_STATIC \ 19 -DSECP256K1_STATIC \
25 -I$(DEPS)/include -I$(RANDOMX)/src \ 20 -I$(DEPS)/include -I$(RANDOMX)/src \
26 -I$(DEPS)/include/nlohmann \ 21 -I$(DEPS)/include/nlohmann \
diff --git a/src/net.cpp b/src/net.cpp
index b9bb95d..b6b990a 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -13,6 +13,8 @@
13#include <thread> // hardware_concurrency, for the miner thread count 13#include <thread> // hardware_concurrency, for the miner thread count
14#ifdef _WIN32 14#ifdef _WIN32
15#include <winsock2.h> 15#include <winsock2.h>
16#else
17#include <poll.h>
16#endif 18#endif
17#include <openssl/rand.h> 19#include <openssl/rand.h>
18#include "btfaddr.h" 20#include "btfaddr.h"
@@ -22,6 +24,20 @@ void ThreadReconnectCachedBtfPeers(void* parg);
22void ThreadMessageHandler2(void* parg); 24void ThreadMessageHandler2(void* parg);
23void ThreadSocketHandler2(void* parg); 25void ThreadSocketHandler2(void* parg);
24 26
27#ifdef _WIN32
28typedef WSAPOLLFD BtfPollFd;
29static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs)
30{
31 return WSAPoll(pfd, nfd, nTimeoutMs);
32}
33#else
34typedef struct pollfd BtfPollFd;
35static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs)
36{
37 return poll(pfd, (nfds_t)nfd, nTimeoutMs);
38}
39#endif
40
25 41
26 42
27 43
@@ -274,8 +290,8 @@ string GetDiagnosticsText()
274 290
275 // The number that would have made the deafness obvious. Anything held but 291 // The number that would have made the deafness obvious. Anything held but
276 // not watched is a socket this node will never read again. 292 // not watched is a socket this node will never read again.
277 str += strprintf(" peers watched %d of %d that select() can hold%s\n", 293 str += strprintf(" peers watched %d of %d held by poll%s\n",
278 nPeersWatched, (int)FD_SETSIZE - 1, 294 nPeersWatched, nHeld,
279 nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : ""); 295 nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : "");
280 296
281 if (nBlocksReceived > 0) 297 if (nBlocksReceived > 0)
@@ -1198,7 +1214,7 @@ void CNode::Disconnect()
1198 1214
1199 // Invalidate before anything else can look at it. This used to close the 1215 // Invalidate before anything else can look at it. This used to close the
1200 // socket and leave the handle number sitting in the object, so ~CNode 1216 // socket and leave the handle number sitting in the object, so ~CNode
1201 // closed it a second time and the select loop kept arming it in the 1217 // closed it a second time and the socket loop kept arming it in the
1202 // meantime. See the note on ~CNode in net.h. 1218 // meantime. See the note on ~CNode in net.h.
1203 BtfCloseSocket(hSocket); 1219 BtfCloseSocket(hSocket);
1204 hSocket = INVALID_SOCKET; 1220 hSocket = INVALID_SOCKET;
@@ -1359,52 +1375,49 @@ void ThreadSocketHandler2(void* parg)
1359 // 1375 //
1360 // Find which sockets have data to receive 1376 // Find which sockets have data to receive
1361 // 1377 //
1362 struct timeval timeout; 1378 static const int nPollTimeoutMs = 50; // frequency to poll pnode->vSend
1363 timeout.tv_sec = 0; 1379
1364 timeout.tv_usec = 50000; // frequency to poll pnode->vSend 1380 struct NodePollResult
1365 1381 {
1366 fd_set fdsetRecv; 1382 CNode* pnode;
1367 fd_set fdsetSend; 1383 SOCKET hSocket;
1368 FD_ZERO(&fdsetRecv); 1384 bool fRecv;
1369 FD_ZERO(&fdsetSend); 1385 bool fSend;
1370 SOCKET hSocketMax = 0; 1386 bool fInvalid;
1371 FD_SET(hListenSocket, &fdsetRecv); 1387 NodePollResult(CNode* pnodeIn, SOCKET hSocketIn)
1372 hSocketMax = max(hSocketMax, hListenSocket); 1388 : pnode(pnodeIn), hSocket(hSocketIn), fRecv(false), fSend(false), fInvalid(false) {}
1373 // FD_SET drops silently once the set is full, so count what goes in 1389 };
1374 // and say so. A peer that never makes it into the set is never read, 1390
1375 // and that is indistinguishable from a peer with nothing to say -- 1391 vector<BtfPollFd> vPoll;
1376 // the failure this whole path used to hide. 1392 vector<NodePollResult> vPollNodes;
1377 unsigned int nWatched = 1; // the listen socket 1393
1394 BtfPollFd listenPoll;
1395 memset(&listenPoll, 0, sizeof(listenPoll));
1396 listenPoll.fd = hListenSocket;
1397 listenPoll.events = POLLIN;
1398 vPoll.push_back(listenPoll);
1399
1378 CRITICAL_BLOCK(cs_vNodes) 1400 CRITICAL_BLOCK(cs_vNodes)
1379 { 1401 {
1380 foreach(CNode* pnode, vNodes) 1402 foreach(CNode* pnode, vNodes)
1381 { 1403 {
1382 if (nWatched >= FD_SETSIZE)
1383 break;
1384 // A disconnected node stays in vNodes until its references go. 1404 // A disconnected node stays in vNodes until its references go.
1385 // Arming its closed handle asks select() to watch a number the 1405 // Do not watch a closed handle number: the OS may already have
1386 // operating system may already have given to somebody else. 1406 // handed it to a different connection.
1387 if (pnode->hSocket == INVALID_SOCKET) 1407 if (pnode->hSocket == INVALID_SOCKET)
1388 continue; 1408 continue;
1389 FD_SET(pnode->hSocket, &fdsetRecv); 1409
1390 nWatched++; 1410 BtfPollFd pfd;
1391 hSocketMax = max(hSocketMax, pnode->hSocket); 1411 memset(&pfd, 0, sizeof(pfd));
1412 pfd.fd = pnode->hSocket;
1413 pfd.events = POLLIN;
1392 TRY_CRITICAL_BLOCK(pnode->cs_vSend) 1414 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
1393 if (!pnode->vSend.empty()) 1415 if (!pnode->vSend.empty())
1394 FD_SET(pnode->hSocket, &fdsetSend); 1416 pfd.events |= POLLOUT;
1395 } 1417 vPoll.push_back(pfd);
1396 if (vNodes.size() + 1 > (size_t)FD_SETSIZE) 1418 vPollNodes.push_back(NodePollResult(pnode, pnode->hSocket));
1397 {
1398 static int64 nLastWarned = 0;
1399 if (GetTime() - nLastWarned > 60)
1400 {
1401 nLastWarned = GetTime();
1402 printf("WARNING: %d peers but select() can only watch %d -- %d are not being read\n",
1403 (int)vNodes.size(), (int)FD_SETSIZE - 1,
1404 (int)vNodes.size() - (int)FD_SETSIZE + 1);
1405 }
1406 } 1419 }
1407 nPeersWatched = (int)nWatched - 1; // less the listen socket 1420 nPeersWatched = (int)vPollNodes.size();
1408 } 1421 }
1409 1422
1410 // Say the state of the node out loud now and then, so a log pulled off 1423 // Say the state of the node out loud now and then, so a log pulled off
@@ -1425,32 +1438,17 @@ void ThreadSocketHandler2(void* parg)
1425 } 1438 }
1426 1439
1427 vfThreadRunning[0] = false; 1440 vfThreadRunning[0] = false;
1428 int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout); 1441 int nPoll = BtfPoll(&vPoll[0], (unsigned int)vPoll.size(), nPollTimeoutMs);
1429 vfThreadRunning[0] = true; 1442 vfThreadRunning[0] = true;
1430 CheckForShutdown(0); 1443 CheckForShutdown(0);
1431 if (nSelect == SOCKET_ERROR) 1444 if (nPoll == SOCKET_ERROR)
1432 { 1445 {
1433 // This used to answer a failed select() by marking every
1434 // descriptor from 0 to hSocketMax ready, which is code written for
1435 // POSIX, where a descriptor is a small integer. A Windows SOCKET is
1436 // a kernel handle in the thousands, so the loop iterated over
1437 // numbers that are not sockets and told the rest of the function
1438 // that every peer had data. One bad handle in the set makes
1439 // select() fail on every pass, so a single closed socket put the
1440 // whole thread into that state permanently.
1441 //
1442 // Find the bad handle instead and let it be dropped.
1443 int nErr = WSAGetLastError(); 1446 int nErr = WSAGetLastError();
1444 LogPrint("net", "select failed: %d\n", nErr); 1447 LogPrint("net", "poll failed: %d\n", nErr);
1445 FD_ZERO(&fdsetRecv);
1446 FD_ZERO(&fdsetSend);
1447 CRITICAL_BLOCK(cs_vNodes) 1448 CRITICAL_BLOCK(cs_vNodes)
1448 { 1449 {
1449 foreach(CNode* pnode, vNodes) 1450 foreach(CNode* pnode, vNodes)
1450 { 1451 {
1451 // Already disconnected: its handle is gone on purpose, and
1452 // reporting it here would name the wrong node for a select()
1453 // failure it did not cause.
1454 if (pnode->hSocket == INVALID_SOCKET) 1452 if (pnode->hSocket == INVALID_SOCKET)
1455 continue; 1453 continue;
1456 int nType = 0; 1454 int nType = 0;
@@ -1467,7 +1465,17 @@ void ThreadSocketHandler2(void* parg)
1467 } 1465 }
1468 } 1466 }
1469 } 1467 }
1470 Sleep(timeout.tv_usec/1000); 1468 Sleep(nPollTimeoutMs);
1469 }
1470 else
1471 {
1472 for (size_t i = 0; i < vPollNodes.size(); i++)
1473 {
1474 short revents = vPoll[i + 1].revents; // poll[0] is the listener
1475 vPollNodes[i].fRecv = (revents & (POLLIN | POLLERR | POLLHUP)) != 0;
1476 vPollNodes[i].fSend = (revents & POLLOUT) != 0;
1477 vPollNodes[i].fInvalid = (revents & POLLNVAL) != 0;
1478 }
1471 } 1479 }
1472 RandAddSeed(); 1480 RandAddSeed();
1473 1481
@@ -1483,7 +1491,7 @@ void ThreadSocketHandler2(void* parg)
1483 // 1491 //
1484 // Accept new connections 1492 // Accept new connections
1485 // 1493 //
1486 if (FD_ISSET(hListenSocket, &fdsetRecv)) 1494 if (nPoll != SOCKET_ERROR && (vPoll[0].revents & POLLIN))
1487 { 1495 {
1488 struct sockaddr_in sockaddr; 1496 struct sockaddr_in sockaddr;
1489#ifdef _WIN32 1497#ifdef _WIN32
@@ -1502,7 +1510,7 @@ void ThreadSocketHandler2(void* parg)
1502 { 1510 {
1503 // Refuse rather than accept a connection this node cannot 1511 // Refuse rather than accept a connection this node cannot
1504 // watch. Without this the listen socket kept taking peers -- 1512 // watch. Without this the listen socket kept taking peers --
1505 // it is always in the select set -- while everything past the 1513 // it is always watched -- while everything past the
1506 // limit sat open and unread, which fed on itself: the deafer 1514 // limit sat open and unread, which fed on itself: the deafer
1507 // the node got, the more connections it collected. 1515 // the node got, the more connections it collected.
1508 unsigned int nNodes = 0; 1516 unsigned int nNodes = 0;
@@ -1529,18 +1537,28 @@ void ThreadSocketHandler2(void* parg)
1529 // 1537 //
1530 // Service each socket 1538 // Service each socket
1531 // 1539 //
1532 vector<CNode*> vNodesCopy; 1540 for (size_t i = 0; i < vPollNodes.size(); i++)
1533 CRITICAL_BLOCK(cs_vNodes)
1534 vNodesCopy = vNodes;
1535 foreach(CNode* pnode, vNodesCopy)
1536 { 1541 {
1537 CheckForShutdown(0); 1542 CheckForShutdown(0);
1538 SOCKET hSocket = pnode->hSocket; 1543 NodePollResult& poll = vPollNodes[i];
1544 CNode* pnode = poll.pnode;
1545 SOCKET hSocket = poll.hSocket;
1546
1547 if (hSocket == INVALID_SOCKET || pnode->hSocket != hSocket)
1548 continue;
1549
1550 if (poll.fInvalid)
1551 {
1552 LogPrint("net", "poll reported invalid socket for %s\n",
1553 pnode->addr.ToString().c_str());
1554 pnode->fDisconnect = true;
1555 continue;
1556 }
1539 1557
1540 // 1558 //
1541 // Receive 1559 // Receive
1542 // 1560 //
1543 if (FD_ISSET(hSocket, &fdsetRecv)) 1561 if (poll.fRecv)
1544 { 1562 {
1545 TRY_CRITICAL_BLOCK(pnode->cs_vRecv) 1563 TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
1546 { 1564 {
@@ -1580,7 +1598,7 @@ void ThreadSocketHandler2(void* parg)
1580 // 1598 //
1581 // Send 1599 // Send
1582 // 1600 //
1583 if (FD_ISSET(hSocket, &fdsetSend)) 1601 if (poll.fSend)
1584 { 1602 {
1585 TRY_CRITICAL_BLOCK(pnode->cs_vSend) 1603 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
1586 { 1604 {
diff --git a/src/net.h b/src/net.h
index bcd0b2a..feecf96 100644
--- a/src/net.h
+++ b/src/net.h
@@ -98,19 +98,16 @@ static const int BTF_INCOMPLETE_MESSAGE_TIMEOUT_SECS = 2 * 60;
98 98
99// Ceiling on simultaneous connections. 99// Ceiling on simultaneous connections.
100// 100//
101// ThreadSocketHandler watches every peer through one select(), and select() 101// ThreadSocketHandler watches every peer in one socket loop. This used to be a
102// cannot watch more descriptors than FD_SETSIZE. On Windows FD_SET simply 102// select() loop, which made FD_SETSIZE the real limit and silently starved
103// stops adding once the set is full -- no error, no log -- so every socket 103// sockets past that bitmap. It is now poll()/WSAPoll(), so the ceiling below is
104// past the limit stays open and is never read again. Nothing capped vNodes, 104// a deliberate resource bound rather than a descriptor-set accident.
105// so a node accepted connections it had no way to service, and because new
106// entries go on the end of vNodes it was always the newest connection that
107// starved, including the ones dialed to fix connectivity.
108// 105//
109// Measured on two nodes before this ceiling existed: 277 and 90 CNode objects 106// Measured on two nodes before this ceiling existed: 277 and 90 CNode objects
110// against 93 and 90 live sockets, and a status bar reporting hundreds of 107// against 93 and 90 live sockets, and a status bar reporting hundreds of
111// peers for a node that was really talking to thirty. 108// peers for a node that was really talking to thirty.
112// 109//
113// 125 is Bitcoin's number and sits far below FD_SETSIZE on both platforms. 110// 125 is Bitcoin's number and remains a conservative public-node default.
114static const unsigned int MAX_CONNECTIONS = 125; 111static const unsigned int MAX_CONNECTIONS = 125;
115 112
116// A node marked for disconnect is normally held until its buffers drain, so 113// A node marked for disconnect is normally held until its buffers drain, so
@@ -142,7 +139,7 @@ static const int BTF_BEHIND_WARN_INTERVAL_SECS = 5 * 60;
142// 139//
143// These counters exist so it can. Cheap to keep, and they name the failures 140// These counters exist so it can. Cheap to keep, and they name the failures
144// that actually happened rather than the ones that sound impressive. 141// that actually happened rather than the ones that sound impressive.
145extern int nPeersWatched; // peers in the last select() set 142extern int nPeersWatched; // peers in the last poll() set
146extern int64 nBlocksReceived; // blocks handed to ProcessBlock 143extern int64 nBlocksReceived; // blocks handed to ProcessBlock
147extern int64 nBlocksWithoutParent; // ...of those, how many arrived orphaned 144extern int64 nBlocksWithoutParent; // ...of those, how many arrived orphaned
148 145