diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/makefile.mingw | 10 | ||||
| -rw-r--r-- | src/net.cpp | 152 | ||||
| -rw-r--r-- | src/net.h | 15 |
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. | ||
| 22 | CXXFLAGS = -std=gnu++14 -O2 $(WARNFLAGS) -fpermissive -pthread \ | 17 | CXXFLAGS = -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); | |||
| 23 | void ThreadMessageHandler2(void* parg); | 25 | void ThreadMessageHandler2(void* parg); |
| 24 | void ThreadSocketHandler2(void* parg); | 26 | void ThreadSocketHandler2(void* parg); |
| 25 | 27 | ||
| 28 | #ifdef _WIN32 | ||
| 29 | typedef WSAPOLLFD BtfPollFd; | ||
| 30 | static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs) | ||
| 31 | { | ||
| 32 | return WSAPoll(pfd, nfd, nTimeoutMs); | ||
| 33 | } | ||
| 34 | #else | ||
| 35 | typedef struct pollfd BtfPollFd; | ||
| 36 | static 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 | { |
| @@ -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. |
| 114 | static const unsigned int MAX_CONNECTIONS = 125; | 111 | static 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. |
| 145 | extern int nPeersWatched; // peers in the last select() set | 142 | extern int nPeersWatched; // peers in the last poll() set |
| 146 | extern int64 nBlocksReceived; // blocks handed to ProcessBlock | 143 | extern int64 nBlocksReceived; // blocks handed to ProcessBlock |
| 147 | extern int64 nBlocksWithoutParent; // ...of those, how many arrived orphaned | 144 | extern int64 nBlocksWithoutParent; // ...of those, how many arrived orphaned |
| 148 | 145 | ||