upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-08-01 13:10:23 -0300
committerGitHub <noreply@github.com>2026-08-01 13:10:23 -0300
commit21c8bb63cb85f3001ddee9237b1d7e689675d293 (patch)
tree9668f6bf39635424a6180c7a7edb6c7931ea7545
parent7131c30c33c980f0ba1d37dfc6a3fbe8c319813e (diff)
diag: count sockets by where they were created, and whether the close took (#86)
A node was found holding 1502 sockets in Windows bound state, one ephemeral port each, 9% of the machine range in nineteen hours. Reading the code did not find it: every connect() failure path here closes its socket. So count. Every socket is tagged at creation and untagged at close, across all ten sites, with a counter for closes that fail. Already rules out three explanations: nothing is created or closed behind the counter (untagged closes are zero), no close is failing, and the leaked sockets are not in the live set -- tracked live matches what the OS reports as established, while the bound pile is something the program already closed successfully.
-rw-r--r--src/btfrv.cpp12
-rw-r--r--src/btftunnel.cpp23
-rw-r--r--src/net.cpp43
-rw-r--r--src/net.h2
-rw-r--r--src/nostr.cpp6
-rw-r--r--src/rpc.cpp4
-rw-r--r--src/sockcount.h137
7 files changed, 199 insertions, 28 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp
index 7efd38b..6ac3280 100644
--- a/src/btfrv.cpp
+++ b/src/btfrv.cpp
@@ -11,7 +11,7 @@
11#ifdef _WIN32 11#ifdef _WIN32
12#include <winsock2.h> 12#include <winsock2.h>
13#include <ws2tcpip.h> 13#include <ws2tcpip.h>
14#define CLOSESOCK closesocket 14#define CLOSESOCK BtfCloseSocket
15#else 15#else
16#include <sys/socket.h> 16#include <sys/socket.h>
17#include <netinet/in.h> 17#include <netinet/in.h>
@@ -22,12 +22,14 @@
22typedef int SOCKET; 22typedef int SOCKET;
23#define INVALID_SOCKET (-1) 23#define INVALID_SOCKET (-1)
24#define SOCKET_ERROR (-1) 24#define SOCKET_ERROR (-1)
25#define CLOSESOCK ::close 25#define CLOSESOCK BtfCloseSocket
26#ifndef SD_BOTH 26#ifndef SD_BOTH
27#define SD_BOTH SHUT_RDWR 27#define SD_BOTH SHUT_RDWR
28#endif 28#endif
29#endif 29#endif
30 30
31#include "sockcount.h"
32
31#include <cstring> 33#include <cstring>
32#include <cstdio> 34#include <cstdio>
33#include <string> 35#include <string>
@@ -96,7 +98,7 @@ static SOCKET ConnectTo(const char* host, unsigned short port)
96 hints.ai_socktype = SOCK_STREAM; 98 hints.ai_socktype = SOCK_STREAM;
97 char portstr[16]; sprintf(portstr, "%u", (unsigned)port); 99 char portstr[16]; sprintf(portstr, "%u", (unsigned)port);
98 if (getaddrinfo(host, portstr, &hints, &res) != 0 || !res) return INVALID_SOCKET; 100 if (getaddrinfo(host, portstr, &hints, &res) != 0 || !res) return INVALID_SOCKET;
99 SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 101 SOCKET s = BtfSocketTag(socket(res->ai_family, res->ai_socktype, res->ai_protocol), SOCK_RV_DIAL);
100 if (s == INVALID_SOCKET) { freeaddrinfo(res); return INVALID_SOCKET; } 102 if (s == INVALID_SOCKET) { freeaddrinfo(res); return INVALID_SOCKET; }
101 103
102 // Apply a short timeout only for the connect() and initial handshake so an 104 // Apply a short timeout only for the connect() and initial handshake so an
@@ -201,7 +203,7 @@ bool RvRelayRun(unsigned short port)
201 if (!RvInit()) return false; 203 if (!RvInit()) return false;
202 g_stop = false; 204 g_stop = false;
203 205
204 g_listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 206 g_listen = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), SOCK_RV_LISTEN);
205 if (g_listen == INVALID_SOCKET) return false; 207 if (g_listen == INVALID_SOCKET) return false;
206 int yes = 1; 208 int yes = 1;
207 setsockopt(g_listen, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); 209 setsockopt(g_listen, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
@@ -218,7 +220,7 @@ bool RvRelayRun(unsigned short port)
218 { 220 {
219 struct sockaddr_in cli; 221 struct sockaddr_in cli;
220 socklen_t len = sizeof(cli); 222 socklen_t len = sizeof(cli);
221 SOCKET s = accept(g_listen, (struct sockaddr*)&cli, &len); 223 SOCKET s = BtfSocketTag(accept(g_listen, (struct sockaddr*)&cli, &len), SOCK_RV_ACCEPT);
222 if (s == INVALID_SOCKET) 224 if (s == INVALID_SOCKET)
223 { 225 {
224 if (g_stop) break; 226 if (g_stop) break;
diff --git a/src/btftunnel.cpp b/src/btftunnel.cpp
index 65c4b89..63d178b 100644
--- a/src/btftunnel.cpp
+++ b/src/btftunnel.cpp
@@ -23,6 +23,7 @@ typedef int SOCKET;
23#define SD_BOTH SHUT_RDWR 23#define SD_BOTH SHUT_RDWR
24#endif 24#endif
25#endif 25#endif
26#include "sockcount.h"
26#include <cstring> 27#include <cstring>
27#include <vector> 28#include <vector>
28#include <thread> 29#include <thread>
@@ -39,7 +40,7 @@ bool TunnelInit()
39// Returns app end and pump end; both are ordinary full-duplex TCP sockets. 40// Returns app end and pump end; both are ordinary full-duplex TCP sockets.
40static bool MakeLocalPair(btf_socket_t& appEnd, btf_socket_t& pumpEnd) 41static bool MakeLocalPair(btf_socket_t& appEnd, btf_socket_t& pumpEnd)
41{ 42{
42 SOCKET listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 43 SOCKET listener = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), SOCK_PAIR_LISTEN);
43 if (listener == INVALID_SOCKET) return false; 44 if (listener == INVALID_SOCKET) return false;
44 45
45 struct sockaddr_in addr; 46 struct sockaddr_in addr;
@@ -47,23 +48,23 @@ static bool MakeLocalPair(btf_socket_t& appEnd, btf_socket_t& pumpEnd)
47 addr.sin_family = AF_INET; 48 addr.sin_family = AF_INET;
48 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 49 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
49 addr.sin_port = 0; // ephemeral 50 addr.sin_port = 0; // ephemeral
50 if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) != 0) { closesocket(listener); return false; } 51 if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) != 0) { BtfCloseSocket(listener); return false; }
51 if (listen(listener, 1) != 0) { closesocket(listener); return false; } 52 if (listen(listener, 1) != 0) { BtfCloseSocket(listener); return false; }
52 53
53#ifdef _WIN32 54#ifdef _WIN32
54 int len = sizeof(addr); 55 int len = sizeof(addr);
55#else 56#else
56 socklen_t len = sizeof(addr); 57 socklen_t len = sizeof(addr);
57#endif 58#endif
58 if (getsockname(listener, (struct sockaddr*)&addr, &len) != 0) { closesocket(listener); return false; } 59 if (getsockname(listener, (struct sockaddr*)&addr, &len) != 0) { BtfCloseSocket(listener); return false; }
59 60
60 SOCKET a = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 61 SOCKET a = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), SOCK_PAIR_APP);
61 if (a == INVALID_SOCKET) { closesocket(listener); return false; } 62 if (a == INVALID_SOCKET) { BtfCloseSocket(listener); return false; }
62 if (connect(a, (struct sockaddr*)&addr, sizeof(addr)) != 0) { closesocket(a); closesocket(listener); return false; } 63 if (connect(a, (struct sockaddr*)&addr, sizeof(addr)) != 0) { BtfCloseSocket(a); BtfCloseSocket(listener); return false; }
63 64
64 SOCKET b = accept(listener, NULL, NULL); 65 SOCKET b = BtfSocketTag(accept(listener, NULL, NULL), SOCK_PAIR_PUMP);
65 closesocket(listener); 66 BtfCloseSocket(listener);
66 if (b == INVALID_SOCKET) { closesocket(a); return false; } 67 if (b == INVALID_SOCKET) { BtfCloseSocket(a); return false; }
67 68
68 appEnd = a; 69 appEnd = a;
69 pumpEnd = b; 70 pumpEnd = b;
@@ -129,7 +130,7 @@ static btf_socket_t StartTunnel(RvSocket rv, const unsigned char shared[32])
129 std::thread(PumpOut, pumpEnd, rv, key).detach(); 130 std::thread(PumpOut, pumpEnd, rv, key).detach();
130 std::thread([pumpEnd, rv, key]() { 131 std::thread([pumpEnd, rv, key]() {
131 PumpIn(pumpEnd, rv, key); 132 PumpIn(pumpEnd, rv, key);
132 closesocket(pumpEnd); 133 BtfCloseSocket(pumpEnd);
133 RvClose(rv); 134 RvClose(rv);
134 }).detach(); 135 }).detach();
135 return appEnd; 136 return appEnd;
diff --git a/src/net.cpp b/src/net.cpp
index 19893ee..7a13257 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -51,6 +51,33 @@ int64 nBlocksReceived = 0;
51int64 nBlocksWithoutParent = 0; 51int64 nBlocksWithoutParent = 0;
52static int64 nNodeStartTime = 0; 52static int64 nNodeStartTime = 0;
53 53
54// --- Socket accounting (implementation lives in sockcount.h) --------------
55static const char* pszSockSite[SOCK_SITES] = {
56 "external-ip probe", "listen socket", "inbound accept",
57 "rendezvous dial", "rendezvous listen", "rendezvous accept",
58 "loopback pair listener", "loopback pair app end", "loopback pair pump end",
59 "nostr relay"
60};
61
62static string SockAccountingText()
63{
64 long long nOpened[SOCK_SITES], nClosed[SOCK_SITES], nFailed[SOCK_SITES], nUntagged = 0;
65 BtfSockSnapshot(nOpened, nClosed, nFailed, &nUntagged);
66
67 string str = "\n sockets by where they were created\n";
68 str += " site opened closed live close failed\n";
69 for (int i = 0; i < SOCK_SITES; i++)
70 {
71 if (nOpened[i] == 0)
72 continue;
73 str += strprintf(" %-24s %8lld %8lld %8lld %13lld\n", pszSockSite[i],
74 nOpened[i], nClosed[i], nOpened[i] - nClosed[i], nFailed[i]);
75 }
76 str += strprintf(" %-24s %8s %8lld %8s %13s\n", "closed but never tagged",
77 "-", nUntagged, "-", "-");
78 return str;
79}
80
54// Defined further down, next to the counter it reads. The miner keeps its own 81// Defined further down, next to the counter it reads. The miner keeps its own
55// live count -- threads bump it on the way in and on the way out -- which is 82// live count -- threads bump it on the way in and on the way out -- which is
56// the honest number here: how many are hashing now, not how many were asked to. 83// the honest number here: how many are hashing now, not how many were asked to.
@@ -119,6 +146,8 @@ string GetDiagnosticsText()
119 (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str() 146 (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str()
120 : ", not mining"); 147 : ", not mining");
121 148
149 str += SockAccountingText();
150
122 str += "\n peer dir height last recv last send unsent\n"; 151 str += "\n peer dir height last recv last send unsent\n";
123 foreach(CNode* pnode, vCopy) 152 foreach(CNode* pnode, vCopy)
124 { 153 {
@@ -157,7 +186,7 @@ bool GetMyExternalIP(unsigned int& ipRet)
157 if (getaddrinfo(svc.host, "80", &hints, &res) != 0 || !res) 186 if (getaddrinfo(svc.host, "80", &hints, &res) != 0 || !res)
158 continue; 187 continue;
159 188
160 SOCKET hSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 189 SOCKET hSocket = BtfSocketTag(socket(res->ai_family, res->ai_socktype, res->ai_protocol), SOCK_EXTIP);
161 if (hSocket == INVALID_SOCKET) { freeaddrinfo(res); continue; } 190 if (hSocket == INVALID_SOCKET) { freeaddrinfo(res); continue; }
162 191
163 // 5-second timeout so a dead service doesn't stall startup 192 // 5-second timeout so a dead service doesn't stall startup
@@ -173,7 +202,7 @@ bool GetMyExternalIP(unsigned int& ipRet)
173 202
174 if (connect(hSocket, res->ai_addr, (int)res->ai_addrlen) != 0) 203 if (connect(hSocket, res->ai_addr, (int)res->ai_addrlen) != 0)
175 { 204 {
176 freeaddrinfo(res); closesocket(hSocket); continue; 205 freeaddrinfo(res); BtfCloseSocket(hSocket); continue;
177 } 206 }
178 freeaddrinfo(res); 207 freeaddrinfo(res);
179 208
@@ -190,7 +219,7 @@ bool GetMyExternalIP(unsigned int& ipRet)
190 response += buf; 219 response += buf;
191 if (response.size() > 4096) break; 220 if (response.size() > 4096) break;
192 } 221 }
193 closesocket(hSocket); 222 BtfCloseSocket(hSocket);
194 223
195 // Find blank line separating headers from body 224 // Find blank line separating headers from body
196 size_t bodyPos = response.find("\r\n\r\n"); 225 size_t bodyPos = response.find("\r\n\r\n");
@@ -950,7 +979,7 @@ void CNode::Disconnect()
950{ 979{
951 LogPrint("net", "disconnecting node %s\n", addr.ToString().c_str()); 980 LogPrint("net", "disconnecting node %s\n", addr.ToString().c_str());
952 981
953 closesocket(hSocket); 982 BtfCloseSocket(hSocket);
954 983
955 // All of a nodes broadcasts and subscriptions are automatically torn down 984 // All of a nodes broadcasts and subscriptions are automatically torn down
956 // when it goes down, so a node has to stay up to keep its broadcast going. 985 // when it goes down, so a node has to stay up to keep its broadcast going.
@@ -1230,7 +1259,7 @@ void ThreadSocketHandler2(void* parg)
1230#else 1259#else
1231 socklen_t len = sizeof(sockaddr); 1260 socklen_t len = sizeof(sockaddr);
1232#endif 1261#endif
1233 SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); 1262 SOCKET hSocket = BtfSocketTag(accept(hListenSocket, (struct sockaddr*)&sockaddr, &len), SOCK_ACCEPT);
1234 CAddress addr(sockaddr); 1263 CAddress addr(sockaddr);
1235 if (hSocket == INVALID_SOCKET) 1264 if (hSocket == INVALID_SOCKET)
1236 { 1265 {
@@ -1251,7 +1280,7 @@ void ThreadSocketHandler2(void* parg)
1251 { 1280 {
1252 LogPrint("net", "refusing connection from %s, already at %u\n", 1281 LogPrint("net", "refusing connection from %s, already at %u\n",
1253 addr.ToString().c_str(), nNodes); 1282 addr.ToString().c_str(), nNodes);
1254 closesocket(hSocket); 1283 BtfCloseSocket(hSocket);
1255 } 1284 }
1256 else 1285 else
1257 { 1286 {
@@ -1639,7 +1668,7 @@ bool StartNode(string& strError)
1639 printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); 1668 printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str());
1640 1669
1641 // Create socket for listening for incoming connections 1670 // Create socket for listening for incoming connections
1642 SOCKET hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 1671 SOCKET hListenSocket = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), SOCK_LISTEN);
1643 if (hListenSocket == INVALID_SOCKET) 1672 if (hListenSocket == INVALID_SOCKET)
1644 { 1673 {
1645 strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); 1674 strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError());
diff --git a/src/net.h b/src/net.h
index bd4eb77..314c709 100644
--- a/src/net.h
+++ b/src/net.h
@@ -121,6 +121,8 @@ extern int64 nBlocksWithoutParent; // ...of those, how many arrived orphaned
121// user pastes into an issue is what a developer already knows how to read. 121// user pastes into an issue is what a developer already knows how to read.
122string GetDiagnosticsText(); 122string GetDiagnosticsText();
123 123
124#include "sockcount.h"
125
124// Defined in main.cpp. Declared here because CNode announces it in the version 126// Defined in main.cpp. Declared here because CNode announces it in the version
125// message, and net.h is included before main.h. 127// message, and net.h is included before main.h.
126extern int nBestHeight; 128extern int nBestHeight;
diff --git a/src/nostr.cpp b/src/nostr.cpp
index 17139ad..dc96be2 100644
--- a/src/nostr.cpp
+++ b/src/nostr.cpp
@@ -462,7 +462,7 @@ public:
462 { 462 {
463 if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); ssl = NULL; } 463 if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); ssl = NULL; }
464 if (sslctx) { SSL_CTX_free(sslctx); sslctx = NULL; } 464 if (sslctx) { SSL_CTX_free(sslctx); sslctx = NULL; }
465 if (hSocket != INVALID_SOCKET) { closesocket(hSocket); hSocket = INVALID_SOCKET; } 465 if (hSocket != INVALID_SOCKET) { BtfCloseSocket(hSocket); hSocket = INVALID_SOCKET; }
466 } 466 }
467 467
468 int RawRead(char* buf, int len) 468 int RawRead(char* buf, int len)
@@ -518,7 +518,7 @@ public:
518 bool fConnected = false; 518 bool fConnected = false;
519 for (rp = res; rp != NULL; rp = rp->ai_next) 519 for (rp = res; rp != NULL; rp = rp->ai_next)
520 { 520 {
521 hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 521 hSocket = BtfSocketTag(socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol), SOCK_NOSTR);
522 if (hSocket == INVALID_SOCKET) 522 if (hSocket == INVALID_SOCKET)
523 continue; 523 continue;
524 524
@@ -542,7 +542,7 @@ public:
542 break; 542 break;
543 } 543 }
544 544
545 closesocket(hSocket); 545 BtfCloseSocket(hSocket);
546 hSocket = INVALID_SOCKET; 546 hSocket = INVALID_SOCKET;
547 } 547 }
548 freeaddrinfo(res); 548 freeaddrinfo(res);
diff --git a/src/rpc.cpp b/src/rpc.cpp
index 97d8429..41c0328 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -46,10 +46,10 @@
46using json = nlohmann::json; 46using json = nlohmann::json;
47 47
48#ifdef _WIN32 48#ifdef _WIN32
49#define sock_close(s) closesocket(s) 49#define sock_close(s) BtfCloseSocket(s)
50#define SEND_FLAGS 0 50#define SEND_FLAGS 0
51#else 51#else
52#define sock_close(s) ::close(s) 52#define sock_close(s) BtfCloseSocket(s)
53#define SEND_FLAGS MSG_NOSIGNAL 53#define SEND_FLAGS MSG_NOSIGNAL
54#endif 54#endif
55 55
diff --git a/src/sockcount.h b/src/sockcount.h
new file mode 100644
index 0000000..19f0827
--- /dev/null
+++ b/src/sockcount.h
@@ -0,0 +1,137 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3//
4// Socket accounting -- which part of the program is holding the sockets.
5//
6// A node on a 32-core machine was found with 1502 sockets in Windows' "bound"
7// state: created, never connected, never closed, each holding a distinct
8// ephemeral port. That is 9% of the machine's whole dynamic port range gone in
9// nineteen hours, and when the range runs out nothing on that machine can open
10// an outbound connection -- not just this program.
11//
12// Reading the code did not find it. Every connect() failure path in this tree
13// closes its socket. So count instead: tag each socket where it is created,
14// untag it where it is closed, and report the difference per site. Whichever
15// site's live count tracks the leak is the answer.
16//
17// Header-only on purpose. btfrv.cpp is compiled a second time, on its own, into
18// the standalone rendezvous relay -- a separate tree that does not link net.cpp.
19// Anything here that needed a .cpp would break that build, and it would break
20// it on the VPS rather than on this machine. Include after the platform socket
21// headers; each of those files defines SOCKET for itself.
22
23#ifndef BITFLASH_SOCKCOUNT_H
24#define BITFLASH_SOCKCOUNT_H
25
26#include <map>
27#include <mutex>
28
29#ifndef _WIN32
30#include <unistd.h>
31#endif
32
33enum
34{
35 SOCK_EXTIP, // GetMyExternalIP probe
36 SOCK_LISTEN, // the node's own listening socket
37 SOCK_ACCEPT, // inbound connections
38 SOCK_RV_DIAL, // outbound to a rendezvous relay
39 SOCK_RV_LISTEN, // rendezvous listener
40 SOCK_RV_ACCEPT, // inbound at the rendezvous
41 SOCK_PAIR_LISTEN, // loopback pair: the temporary listener
42 SOCK_PAIR_APP, // loopback pair: the app end handed to CNode
43 SOCK_PAIR_PUMP, // loopback pair: the pump end
44 SOCK_NOSTR, // outbound to a Nostr relay
45 SOCK_SITES
46};
47
48struct BtfSockAccount
49{
50 std::mutex mtx;
51 std::map<SOCKET, int> mapSite;
52 long long nOpened[SOCK_SITES];
53 long long nClosed[SOCK_SITES];
54 long long nCloseFailed[SOCK_SITES];
55 long long nClosedUntagged;
56 BtfSockAccount() : nClosedUntagged(0)
57 {
58 for (int i = 0; i < SOCK_SITES; i++) { nOpened[i] = 0; nClosed[i] = 0; nCloseFailed[i] = 0; }
59 }
60};
61
62// One instance across every translation unit: a function-local static in an
63// inline function is the same object everywhere it is used.
64inline BtfSockAccount& BtfSockAccounting()
65{
66 static BtfSockAccount account;
67 return account;
68}
69
70// Record a freshly created socket and return it unchanged, so call sites read
71// as they did before. INVALID_SOCKET passes through untouched.
72inline SOCKET BtfSocketTag(SOCKET hSocket, int nSite)
73{
74 if (hSocket == INVALID_SOCKET || nSite < 0 || nSite >= SOCK_SITES)
75 return hSocket;
76 BtfSockAccount& a = BtfSockAccounting();
77 std::lock_guard<std::mutex> lock(a.mtx);
78 a.mapSite[hSocket] = nSite;
79 a.nOpened[nSite]++;
80 return hSocket;
81}
82
83// Close a socket and forget it. Closing one nobody tagged is counted too --
84// that number staying at zero is what says the accounting is complete.
85inline int BtfCloseSocket(SOCKET hSocket)
86{
87 if (hSocket == INVALID_SOCKET)
88 return 0;
89 int nSite = -1;
90 {
91 BtfSockAccount& a = BtfSockAccounting();
92 std::lock_guard<std::mutex> lock(a.mtx);
93 std::map<SOCKET, int>::iterator mi = a.mapSite.find(hSocket);
94 if (mi == a.mapSite.end())
95 a.nClosedUntagged++;
96 else
97 {
98 nSite = mi->second;
99 a.nClosed[nSite]++;
100 a.mapSite.erase(mi);
101 }
102 }
103#ifdef _WIN32
104 int nRet = ::closesocket(hSocket);
105#else
106 int nRet = ::close(hSocket);
107#endif
108 // A close that fails leaves the descriptor -- and on Windows the ephemeral
109 // port it holds -- in place. Counted because a socket the program believes
110 // it released and the operating system still shows is exactly the shape of
111 // the leak being chased, and nothing in this codebase has ever looked at
112 // what closesocket returns.
113 if (nRet != 0 && nSite >= 0)
114 {
115 BtfSockAccount& a = BtfSockAccounting();
116 std::lock_guard<std::mutex> lock(a.mtx);
117 a.nCloseFailed[nSite]++;
118 }
119 return nRet;
120}
121
122// Copy the counters out. Formatting lives in net.cpp, which has strprintf --
123// and which the relay build does not compile.
124inline void BtfSockSnapshot(long long* pOpened, long long* pClosed, long long* pFailed, long long* pUntagged)
125{
126 BtfSockAccount& a = BtfSockAccounting();
127 std::lock_guard<std::mutex> lock(a.mtx);
128 for (int i = 0; i < SOCK_SITES; i++)
129 {
130 pOpened[i] = a.nOpened[i];
131 pClosed[i] = a.nClosed[i];
132 pFailed[i] = a.nCloseFailed[i];
133 }
134 *pUntagged = a.nClosedUntagged;
135}
136
137#endif