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.mingw10
-rw-r--r--src/net.cpp152
-rw-r--r--src/net.h15
3 files changed, 96 insertions, 81 deletions
diff --git a/src/makefile.mingw b/src/makefile.mingw
index 28e925c..4e36a5c 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -14,14 +14,14 @@ 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 \
18<<<<<<< HEAD
19 -DWIN32 -DNOPCH \
20 -DSECP256K1_STATIC \
21=======
23 -DWIN32 -DNOPCH -DFD_SETSIZE=1024 \ 22 -DWIN32 -DNOPCH -DFD_SETSIZE=1024 \
24 -DSECP256K1_STATIC -DBITFLASH_NODE_BUILD \ 23 -DSECP256K1_STATIC -DBITFLASH_NODE_BUILD \
24>>>>>>> origin/main
25 -I$(DEPS)/include -I$(RANDOMX)/src \ 25 -I$(DEPS)/include -I$(RANDOMX)/src \
26 -I$(DEPS)/include/nlohmann \ 26 -I$(DEPS)/include/nlohmann \
27 -IC:/msys64/ucrt64/include \ 27 -IC:/msys64/ucrt64/include \
diff --git a/src/net.cpp b/src/net.cpp
index fe9964e..c24547e 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"
@@ -23,6 +25,20 @@ void ThreadReconnectCachedBtfPeers(void* parg);
23void ThreadMessageHandler2(void* parg); 25void ThreadMessageHandler2(void* parg);
24void ThreadSocketHandler2(void* parg); 26void ThreadSocketHandler2(void* parg);
25 27
28#ifdef _WIN32
29typedef WSAPOLLFD BtfPollFd;
30static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs)
31{
32 return WSAPoll(pfd, nfd, nTimeoutMs);
33}
34#else
35typedef struct pollfd BtfPollFd;
36static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs)
37{
38 return poll(pfd, (nfds_t)nfd, nTimeoutMs);
39}
40#endif
41
26 42
27 43
28 44
@@ -275,8 +291,8 @@ string GetDiagnosticsText()
275 291
276 // The number that would have made the deafness obvious. Anything held but 292 // The number that would have made the deafness obvious. Anything held but
277 // not watched is a socket this node will never read again. 293 // not watched is a socket this node will never read again.
278 str += strprintf(" peers watched %d of %d that select() can hold%s\n", 294 str += strprintf(" peers watched %d of %d held by poll%s\n",
279 nPeersWatched, (int)FD_SETSIZE - 1, 295 nPeersWatched, nHeld,
280 nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : ""); 296 nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : "");
281 297
282 if (nBlocksReceived > 0) 298 if (nBlocksReceived > 0)
@@ -1202,7 +1218,7 @@ void CNode::Disconnect()
1202 1218
1203 // Invalidate before anything else can look at it. This used to close the 1219 // Invalidate before anything else can look at it. This used to close the
1204 // socket and leave the handle number sitting in the object, so ~CNode 1220 // socket and leave the handle number sitting in the object, so ~CNode
1205 // closed it a second time and the select loop kept arming it in the 1221 // closed it a second time and the socket loop kept arming it in the
1206 // meantime. See the note on ~CNode in net.h. 1222 // meantime. See the note on ~CNode in net.h.
1207 BtfCloseSocket(hSocket); 1223 BtfCloseSocket(hSocket);
1208 hSocket = INVALID_SOCKET; 1224 hSocket = INVALID_SOCKET;
@@ -1363,52 +1379,49 @@ void ThreadSocketHandler2(void* parg)
1363 // 1379 //
1364 // Find which sockets have data to receive 1380 // Find which sockets have data to receive
1365 // 1381 //
1366 struct timeval timeout; 1382 static const int nPollTimeoutMs = 50; // frequency to poll pnode->vSend
1367 timeout.tv_sec = 0; 1383
1368 timeout.tv_usec = 50000; // frequency to poll pnode->vSend 1384 struct NodePollResult
1369 1385 {
1370 fd_set fdsetRecv; 1386 CNode* pnode;
1371 fd_set fdsetSend; 1387 SOCKET hSocket;
1372 FD_ZERO(&fdsetRecv); 1388 bool fRecv;
1373 FD_ZERO(&fdsetSend); 1389 bool fSend;
1374 SOCKET hSocketMax = 0; 1390 bool fInvalid;
1375 FD_SET(hListenSocket, &fdsetRecv); 1391 NodePollResult(CNode* pnodeIn, SOCKET hSocketIn)
1376 hSocketMax = max(hSocketMax, hListenSocket); 1392 : pnode(pnodeIn), hSocket(hSocketIn), fRecv(false), fSend(false), fInvalid(false) {}
1377 // FD_SET drops silently once the set is full, so count what goes in 1393 };
1378 // and say so. A peer that never makes it into the set is never read, 1394
1379 // and that is indistinguishable from a peer with nothing to say -- 1395 vector<BtfPollFd> vPoll;
1380 // the failure this whole path used to hide. 1396 vector<NodePollResult> vPollNodes;
1381 unsigned int nWatched = 1; // the listen socket 1397
1398 BtfPollFd listenPoll;
1399 memset(&listenPoll, 0, sizeof(listenPoll));
1400 listenPoll.fd = hListenSocket;
1401 listenPoll.events = POLLIN;
1402 vPoll.push_back(listenPoll);
1403
1382 CRITICAL_BLOCK(cs_vNodes) 1404 CRITICAL_BLOCK(cs_vNodes)
1383 { 1405 {
1384 foreach(CNode* pnode, vNodes) 1406 foreach(CNode* pnode, vNodes)
1385 { 1407 {
1386 if (nWatched >= FD_SETSIZE)
1387 break;
1388 // A disconnected node stays in vNodes until its references go. 1408 // A disconnected node stays in vNodes until its references go.
1389 // Arming its closed handle asks select() to watch a number the 1409 // Do not watch a closed handle number: the OS may already have
1390 // operating system may already have given to somebody else. 1410 // handed it to a different connection.
1391 if (pnode->hSocket == INVALID_SOCKET) 1411 if (pnode->hSocket == INVALID_SOCKET)
1392 continue; 1412 continue;
1393 FD_SET(pnode->hSocket, &fdsetRecv); 1413
1394 nWatched++; 1414 BtfPollFd pfd;
1395 hSocketMax = max(hSocketMax, pnode->hSocket); 1415 memset(&pfd, 0, sizeof(pfd));
1416 pfd.fd = pnode->hSocket;
1417 pfd.events = POLLIN;
1396 TRY_CRITICAL_BLOCK(pnode->cs_vSend) 1418 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
1397 if (!pnode->vSend.empty()) 1419 if (!pnode->vSend.empty())
1398 FD_SET(pnode->hSocket, &fdsetSend); 1420 pfd.events |= POLLOUT;
1399 } 1421 vPoll.push_back(pfd);
1400 if (vNodes.size() + 1 > (size_t)FD_SETSIZE) 1422 vPollNodes.push_back(NodePollResult(pnode, pnode->hSocket));
1401 {
1402 static int64 nLastWarned = 0;
1403 if (GetTime() - nLastWarned > 60)
1404 {
1405 nLastWarned = GetTime();
1406 printf("WARNING: %d peers but select() can only watch %d -- %d are not being read\n",
1407 (int)vNodes.size(), (int)FD_SETSIZE - 1,
1408 (int)vNodes.size() - (int)FD_SETSIZE + 1);
1409 }
1410 } 1423 }
1411 nPeersWatched = (int)nWatched - 1; // less the listen socket 1424 nPeersWatched = (int)vPollNodes.size();
1412 } 1425 }
1413 1426
1414 // Say the state of the node out loud now and then, so a log pulled off 1427 // Say the state of the node out loud now and then, so a log pulled off
@@ -1429,32 +1442,17 @@ void ThreadSocketHandler2(void* parg)
1429 } 1442 }
1430 1443
1431 vfThreadRunning[0] = false; 1444 vfThreadRunning[0] = false;
1432 int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout); 1445 int nPoll = BtfPoll(&vPoll[0], (unsigned int)vPoll.size(), nPollTimeoutMs);
1433 vfThreadRunning[0] = true; 1446 vfThreadRunning[0] = true;
1434 CheckForShutdown(0); 1447 CheckForShutdown(0);
1435 if (nSelect == SOCKET_ERROR) 1448 if (nPoll == SOCKET_ERROR)
1436 { 1449 {
1437 // This used to answer a failed select() by marking every
1438 // descriptor from 0 to hSocketMax ready, which is code written for
1439 // POSIX, where a descriptor is a small integer. A Windows SOCKET is
1440 // a kernel handle in the thousands, so the loop iterated over
1441 // numbers that are not sockets and told the rest of the function
1442 // that every peer had data. One bad handle in the set makes
1443 // select() fail on every pass, so a single closed socket put the
1444 // whole thread into that state permanently.
1445 //
1446 // Find the bad handle instead and let it be dropped.
1447 int nErr = WSAGetLastError(); 1450 int nErr = WSAGetLastError();
1448 LogPrint("net", "select failed: %d\n", nErr); 1451 LogPrint("net", "poll failed: %d\n", nErr);
1449 FD_ZERO(&fdsetRecv);
1450 FD_ZERO(&fdsetSend);
1451 CRITICAL_BLOCK(cs_vNodes) 1452 CRITICAL_BLOCK(cs_vNodes)
1452 { 1453 {
1453 foreach(CNode* pnode, vNodes) 1454 foreach(CNode* pnode, vNodes)
1454 { 1455 {
1455 // Already disconnected: its handle is gone on purpose, and
1456 // reporting it here would name the wrong node for a select()
1457 // failure it did not cause.
1458 if (pnode->hSocket == INVALID_SOCKET) 1456 if (pnode->hSocket == INVALID_SOCKET)
1459 continue; 1457 continue;
1460 int nType = 0; 1458 int nType = 0;
@@ -1471,7 +1469,17 @@ void ThreadSocketHandler2(void* parg)
1471 } 1469 }
1472 } 1470 }
1473 } 1471 }
1474 Sleep(timeout.tv_usec/1000); 1472 Sleep(nPollTimeoutMs);
1473 }
1474 else
1475 {
1476 for (size_t i = 0; i < vPollNodes.size(); i++)
1477 {
1478 short revents = vPoll[i + 1].revents; // poll[0] is the listener
1479 vPollNodes[i].fRecv = (revents & (POLLIN | POLLERR | POLLHUP)) != 0;
1480 vPollNodes[i].fSend = (revents & POLLOUT) != 0;
1481 vPollNodes[i].fInvalid = (revents & POLLNVAL) != 0;
1482 }
1475 } 1483 }
1476 RandAddSeed(); 1484 RandAddSeed();
1477 1485
@@ -1487,7 +1495,7 @@ void ThreadSocketHandler2(void* parg)
1487 // 1495 //
1488 // Accept new connections 1496 // Accept new connections
1489 // 1497 //
1490 if (FD_ISSET(hListenSocket, &fdsetRecv)) 1498 if (nPoll != SOCKET_ERROR && (vPoll[0].revents & POLLIN))
1491 { 1499 {
1492 struct sockaddr_in sockaddr; 1500 struct sockaddr_in sockaddr;
1493#ifdef _WIN32 1501#ifdef _WIN32
@@ -1506,7 +1514,7 @@ void ThreadSocketHandler2(void* parg)
1506 { 1514 {
1507 // Refuse rather than accept a connection this node cannot 1515 // Refuse rather than accept a connection this node cannot
1508 // watch. Without this the listen socket kept taking peers -- 1516 // watch. Without this the listen socket kept taking peers --
1509 // it is always in the select set -- while everything past the 1517 // it is always watched -- while everything past the
1510 // limit sat open and unread, which fed on itself: the deafer 1518 // limit sat open and unread, which fed on itself: the deafer
1511 // the node got, the more connections it collected. 1519 // the node got, the more connections it collected.
1512 unsigned int nNodes = 0; 1520 unsigned int nNodes = 0;
@@ -1533,18 +1541,28 @@ void ThreadSocketHandler2(void* parg)
1533 // 1541 //
1534 // Service each socket 1542 // Service each socket
1535 // 1543 //
1536 vector<CNode*> vNodesCopy; 1544 for (size_t i = 0; i < vPollNodes.size(); i++)
1537 CRITICAL_BLOCK(cs_vNodes)
1538 vNodesCopy = vNodes;
1539 foreach(CNode* pnode, vNodesCopy)
1540 { 1545 {
1541 CheckForShutdown(0); 1546 CheckForShutdown(0);
1542 SOCKET hSocket = pnode->hSocket; 1547 NodePollResult& poll = vPollNodes[i];
1548 CNode* pnode = poll.pnode;
1549 SOCKET hSocket = poll.hSocket;
1550
1551 if (hSocket == INVALID_SOCKET || pnode->hSocket != hSocket)
1552 continue;
1553
1554 if (poll.fInvalid)
1555 {
1556 LogPrint("net", "poll reported invalid socket for %s\n",
1557 pnode->addr.ToString().c_str());
1558 pnode->fDisconnect = true;
1559 continue;
1560 }
1543 1561
1544 // 1562 //
1545 // Receive 1563 // Receive
1546 // 1564 //
1547 if (FD_ISSET(hSocket, &fdsetRecv)) 1565 if (poll.fRecv)
1548 { 1566 {
1549 TRY_CRITICAL_BLOCK(pnode->cs_vRecv) 1567 TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
1550 { 1568 {
@@ -1584,7 +1602,7 @@ void ThreadSocketHandler2(void* parg)
1584 // 1602 //
1585 // Send 1603 // Send
1586 // 1604 //
1587 if (FD_ISSET(hSocket, &fdsetSend)) 1605 if (poll.fSend)
1588 { 1606 {
1589 TRY_CRITICAL_BLOCK(pnode->cs_vSend) 1607 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
1590 { 1608 {
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