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:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-08-12 16:33:35 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-23 04:37:36 +0000
commit2deef1cfb03a3b5217e46236ae850093089be6f3 (patch)
tree970fd587d50aeb333edad49661df5689a032ce19 /src
parent14e2d7f9ac83d22ea7231fd9f69b36a204afd721 (diff)
net: one dead descriptor must not cost every peer its turn
From the first of two wedges captured on the pool. It is a different proximate fault from the second one, and this is the part that turns a single bad socket into a node-wide outage. A failed select() zeroes both descriptor sets, so no peer is serviced on that pass. That is correct as far as it goes. What was missing is that nothing stopped the offending descriptor coming straight back: - the invalid socket was only marked fDisconnect, and a node is not removed from vNodes until it has drained or DISCONNECT_DRAIN_SECS has elapsed -- and it cannot drain, because its socket is gone; - the select set was rebuilt from vNodes with no check, so the same dead descriptor went back in on the very next pass and failed select() again. Measured in the capture: 1,027 consecutive 'its socket is no longer valid' passes across 61 seconds -- the full drain timeout -- one bad socket, and for the whole of it the node read nothing from anybody. The peer that caused it had 57 bytes unsent, so it took the slow path out. Now the descriptor is forgotten as soon as getsockopt says it is not ours: select() succeeds again on the next pass and every other peer is served normally while the dead node ages out as before. Deliberately forgotten rather than closed. getsockopt has just said this descriptor is no longer ours; calling close() on it is precisely how an unrelated socket gets closed instead, which is the fault fixed in e742d0b. (cherry picked from commit f14c513b4e39d6203b73584f6dc01acbb5a9546e)
Diffstat (limited to 'src')
-rw-r--r--src/net.cpp24
1 files changed, 23 insertions, 1 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 964097b..77f9acf 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1471,11 +1471,29 @@ void ThreadSocketHandler2(void* parg)
1471#else 1471#else
1472 socklen_t nTypeLen = sizeof(nType); 1472 socklen_t nTypeLen = sizeof(nType);
1473#endif 1473#endif
1474 if (getsockopt(pnode->hSocket, SOL_SOCKET, SO_TYPE, (char*)&nType, &nTypeLen) != 0) 1474 if (pnode->hSocket != INVALID_SOCKET &&
1475 getsockopt(pnode->hSocket, SOL_SOCKET, SO_TYPE, (char*)&nType, &nTypeLen) != 0)
1475 { 1476 {
1476 printf("dropping node %s: its socket is no longer valid\n", 1477 printf("dropping node %s: its socket is no longer valid\n",
1477 pnode->addr.ToString().c_str()); 1478 pnode->addr.ToString().c_str());
1478 pnode->fDisconnect = true; 1479 pnode->fDisconnect = true;
1480
1481 // Stop referring to it, or it goes back into the next
1482 // select() set and fails it again.
1483 //
1484 // Marking fDisconnect is not enough: a node is only
1485 // removed from vNodes once it has drained or once
1486 // DISCONNECT_DRAIN_SECS has passed, so one dead
1487 // descriptor kept failing select() for a full minute --
1488 // and a failed select() zeroes both sets above, which
1489 // means NO peer is read on that pass. Measured on a
1490 // wedged pool: 1,027 consecutive passes over 61
1491 // seconds, one bad socket, the entire node deaf.
1492 //
1493 // Not closed, only forgotten. getsockopt just told us
1494 // this descriptor is not ours any more; closing it now
1495 // is how an unrelated socket gets closed instead.
1496 pnode->hSocket = INVALID_SOCKET;
1479 } 1497 }
1480 } 1498 }
1481 } 1499 }
@@ -1548,6 +1566,10 @@ void ThreadSocketHandler2(void* parg)
1548 { 1566 {
1549 CheckForShutdown(0); 1567 CheckForShutdown(0);
1550 SOCKET hSocket = pnode->hSocket; 1568 SOCKET hSocket = pnode->hSocket;
1569 // Forgotten above because it was already invalid. FD_ISSET on it
1570 // is meaningless and recv()/send() would be operating on -1.
1571 if (hSocket == INVALID_SOCKET)
1572 continue;
1551 1573
1552 // 1574 //
1553 // Receive 1575 // Receive