diff options
| author | Nsable-sh <nsable-sh@noreply.git.bitflash.network> | 2026-08-11 16:38:49 -0300 |
|---|---|---|
| committer | Nsable-sh <nsable-sh@noreply.git.bitflash.network> | 2026-08-11 16:38:49 -0300 |
| commit | c5f6b52c0d1d65f7bf8cd745a7a212bc6b2fe318 (patch) | |
| tree | 7ad2835cd6cdca8808b202036525d0cde2380607 /src/net.cpp | |
| parent | a83fae37099cb102beb5a4c174a4abe6ef3f24a4 (diff) | |
net: replace peer select loop with poll
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 152 |
1 files changed, 85 insertions, 67 deletions
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); | |||
| 22 | void ThreadMessageHandler2(void* parg); | 24 | void ThreadMessageHandler2(void* parg); |
| 23 | void ThreadSocketHandler2(void* parg); | 25 | void ThreadSocketHandler2(void* parg); |
| 24 | 26 | ||
| 27 | #ifdef _WIN32 | ||
| 28 | typedef WSAPOLLFD BtfPollFd; | ||
| 29 | static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs) | ||
| 30 | { | ||
| 31 | return WSAPoll(pfd, nfd, nTimeoutMs); | ||
| 32 | } | ||
| 33 | #else | ||
| 34 | typedef struct pollfd BtfPollFd; | ||
| 35 | static 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 | { |