Assertion Failed: pSock->m_callback.m_fnCallback (socketthread.cpp:2609) after re-initializing the library with GameNetworkingSockets_Kill + GameNetworkingSockets_Init
I maintainer di solito rispondono entro 2 giorni
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 45/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- cpp
- Ambito
- networking
Direzione di ricerca
Start with src/steamnetworkingsockets/clientlib/steamnetworkingsockets_socketthread.cpp, especially PollRawUDPSockets() and the deferred cleanup path named in the report. Reproduce with the supplied three-file CMake project on Windows using the no-wait Init/Kill loop, then compare with the 10 ms wait case. Done means repeated Init/Kill cycles no longer trigger the assertion while the reproducer still closes listen sockets successfully.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
In a process that has already called GameNetworkingSockets_Init and GameNetworkingSockets_Kill once, a second Init followed by creating and immediately closing a listen socket sometimes makes the service thread report:
src/steamnetworkingsockets/clientlib/steamnetworkingsockets_socketthread.cpp(2609): Assertion Failed: pSock->m_callback.m_fnCallback
- Not observed after the first
Init: 0 of 500 processes thatInitonce. - Observed after a re-
Init: 18 of 200 processes thatInittwice. - Not observed when the code waits 10 ms after
Initbefore creating the socket.
No crash or functional failure was observed; the report comes from the DBGFLAG_ASSERT sanity check, which tier0/dbg.h compiles into release builds by default.
Versions
- GameNetworkingSockets v1.6.1 (tag commit
d534c19aa760df3fb75fd20db13ba1932b8a5463). On 2026-10-24 this is also the head ofmaster, andsteamnetworkingsockets_socketthread.cppatmasteris byte-identical to the v1.6.1 copy we built. - The library was built with vcpkg (commit
65a2c2391af39b773f83f91e0ab6c8ced8f9c727), portgamenetworkingsockets1.6.1 withdefault-features: false, tripletx64-windows(OpenSSL 3.6.5, protobuf 7.36.2). - Windows 11 Pro x64 (build 26300), Visual Studio 2026 (MSVC 19.51.36260), CMake 4.4.4, Release configuration.
Reproducer (public API only)
Three files in an empty directory.
main.cpp:
// Repeats GameNetworkingSockets_Init -> CreateListenSocketIP -> CloseListenSocket -> GameNetworkingSockets_Kill
// and counts "Assertion Failed" messages reported through the debug output callback.
// Usage: gns_listen_close_repro [iterations=400] [wait_ms_after_init=0] [port=47900]
// Prints one JSON line: {"iterations":..,"wait_ms":..,"listen_failures":..,"assertions":..,"first_assert":".."}
#include <steam/isteamnetworkingutils.h>
#include <steam/steamnetworkingsockets.h>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <string>
#include <thread>
static std::atomic<int> g_asserts{0};
static std::mutex g_mutex;
static std::string g_first_assert;
static void on_debug(ESteamNetworkingSocketsDebugOutputType, const char* msg) {
if (!std::strstr(msg, "Assertion Failed")) return;
if (g_asserts++ == 0) {
std::lock_guard<std::mutex> lock(g_mutex);
const char* a = std::strstr(msg, "Assertion Failed");
g_first_assert = a;
while (!g_first_assert.empty() && (g_first_assert.back() == '\n' || g_first_assert.back() == '\r')) g_first_assert.pop_back();
}
}
int main(int argc, char** argv) {
const int iterations = argc > 1 ? std::atoi(argv[1]) : 400;
const int wait_ms = argc > 2 ? std::atoi(argv[2]) : 0;
const int port = argc > 3 ? std::atoi(argv[3]) : 47900;
int listen_failures = 0;
for (int i = 0; i < iterations; ++i) {
SteamDatagramErrMsg err{};
if (!GameNetworkingSockets_Init(nullptr, err)) {
std::fprintf(stderr, "GameNetworkingSockets_Init failed: %s\n", err);
return 2;
}
SteamNetworkingUtils()->SetDebugOutputFunction(k_ESteamNetworkingSocketsDebugOutputType_Warning, on_debug);
if (wait_ms > 0) std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms));
SteamNetworkingIPAddr addr;
addr.Clear();
addr.SetIPv4(0x7f000001, static_cast<uint16>(port)); // 127.0.0.1
const HSteamListenSocket s = SteamNetworkingSockets()->CreateListenSocketIP(addr, 0, nullptr);
if (s == k_HSteamListenSocket_Invalid) ++listen_failures;
else SteamNetworkingSockets()->CloseListenSocket(s);
GameNetworkingSockets_Kill();
}
std::lock_guard<std::mutex> lock(g_mutex);
std::printf("{\"iterations\": %d, \"wait_ms\": %d, \"listen_failures\": %d, \"assertions\": %d, \"first_assert\": \"%s\"}\n",
iterations, wait_ms, listen_failures, g_asserts.load(), g_first_assert.c_str());
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.21)
project(gns_listen_close_repro CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(GameNetworkingSockets CONFIG REQUIRED)
add_executable(gns_listen_close_repro main.cpp)
target_link_libraries(gns_listen_close_repro PRIVATE GameNetworkingSockets::shared)
vcpkg.json:
{
"name": "gns-listen-close-repro",
"version-string": "1.0.0",
"builtin-baseline": "65a2c2391af39b773f83f91e0ab6c8ced8f9c727",
"dependencies": [
{ "name": "gamenetworkingsockets", "default-features": false }
]
}
Build and run on Windows
From a "Developer PowerShell for VS" (or any shell with CMake and a VS 2022/2026 toolchain), with vcpkg cloned to $env:USERPROFILE\vcpkg and bootstrapped:
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="$env:USERPROFILE\vcpkg\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows
cmake --build build --config Release
.\build\Release\gns_listen_close_repro.exe 400 0 # 400 Init/Kill cycles, no wait after Init
.\build\Release\gns_listen_close_repro.exe 400 10 # the same with a 10 ms wait after Init
.\build\Release\gns_listen_close_repro.exe 1 0 # first Init only (run many times, one process each)
.\build\Release\gns_listen_close_repro.exe 2 0 # one re-Init (run many times, one process each)
The first configure builds OpenSSL, protobuf and abseil through vcpkg, which can take a while; later builds reuse the vcpkg binary cache. The program only binds 127.0.0.1:47900 (third argument changes the port).
Expected: "assertions": 0 in every run.
Actual: non-zero counts in the runs without a wait (see Results).
Results
| Scenario | Processes | Assertions | Processes with ≥1 assertion |
|---|---|---|---|
1 iteration per process (first Init only), no wait |
300 | 0 | 0 |
2 iterations per process (one re-Init), no wait |
200 | 18 | 18 |
| 400 iterations, no wait | 3 | 116 (39, 38, 39) | 3 |
400 iterations, 10 ms wait after Init |
3 | 0 (0, 0, 0) | 0 |
Recorded runs. The rows above come from one batch whose per-process JSON lines were written to a file as they ran. An earlier batch, also recorded to a file, gave the same picture:
- 400 iterations with no wait: 44, 35 and 34 assertions;
- 10 ms wait: 0, 0, 0;
- 50 ms wait: 0, 0, 0.
Both batches used an earlier build of this program. It runs the same loop; only the name of one output field (settle_ms instead of wait_ms) and the formatting of the assert text differ.
Console-only observations. These were not written to a file:
- 400 iterations with no wait: 11, 18 and 21 assertions, plus 40 with the exact
main.cppabove in a fresh build; - 10 ms wait with the exact
main.cpp: 0; - first
Initonly: 0 assertions in another 500 processes; - one re-
Init: 25 of another 200 processes asserted.
What we looked at in the source (a hypothesis, not a confirmed cause)
- The check that fires.
PollRawUDPSockets()begins with aDBGFLAG_ASSERTloop,Assert( pSock->m_callback.m_fnCallback ), over every entry ins_vecRawSockets. - What makes the callback null.
CRawUDPSocketImpl::Close()→InternalAddToCleanupQueue()setsm_callback.m_fnCallback = nullptr. The comment there calls this the marker for a socket "pending destruction". When the service thread is running, the actual deletion is deferred toProcessPendingDestroyClosedRawUDPSockets(), which runs fromProcessDeferredOperations()after polling. - Hypothesis. A socket that is legitimately pending destruction can be seen by that sanity loop before the deferred cleanup runs. The fact that it only happens after a re-
Initsuggests the window depends on state or timing carried over from the previous library lifetime: for example how the newly started service thread interleaves with the first close, or a static that is not reset byKill. - Similar, but not the same assert. #230 (closed) reported static state that
Killdid not reset (s_bRecreatePollList, POSIX).
We have not confirmed any of this with a debugger, a stack trace, or instrumentation of the library, so please treat it as a hypothesis. If the analysis is right, skipping sockets with a null callback in that sanity loop (they are pending destruction by design), or processing pending destructions before the check, would avoid the false positive. Happy to test a patch.
Impact
No crash or misbehaviour observed; our application tests pass. We currently avoid it by initializing the library once per process (no Kill/re-Init cycles) and waiting 50 ms after Init. That workaround is empirical.
Duplicate search
Searched open and closed issues and PRs on 2026-10-24 for: m_fnCallback, m_callback.m_fnCallback, PollRawUDPSockets, ProcessPendingDestroyClosedRawUDPSockets, socketthread.cpp Assertion, CloseListenSocket assert, pending destruction socket assert, Assertion Failed Kill Init, 2609, Assertion Failed. No issue reports this assert. #230 is related (re-initialization state), but it is a different assert and platform.
- Lingua principale
- C++
- Stelle
- 10k
- Fork
- 767
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di ValveSoftware/GameNetworkingSockets
-
ICE (native client): relay-only connections time out against a real TURN server; host-socket checks starve the relay pairsForse già presa @Mr444-dev l’ha presa 4 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 79/100
ValveSoftware/GameNetworkingSockets#452 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 4/5 Più di una settimana Idoneità per principianti 12/100
ValveSoftware/GameNetworkingSockets#465 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 18/100
ValveSoftware/GameNetworkingSockets#464 ·
I maintainer di solito rispondono entro 2 giorni
-
ISteamNetworkingSockets: Trying to use over 8 network lanes/channels failsForse già presa @jpggvilaca l’ha presa 14 giorni fa. Aperta
Difficoltà 4/5 1-2 giorni Idoneità per principianti 55/100
ValveSoftware/GameNetworkingSockets#455 ·
I maintainer di solito rispondono entro 2 giorni
-
ConfigureConnectionLanes: reconfiguring lanes can leave one lane in a stale priority classForse già presa @sebxt l’ha presa 18 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 35/100
ValveSoftware/GameNetworkingSockets#453 ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di ValveSoftware/GameNetworkingSockets
Issue simili
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
Sunrisepeak/mcpp-language-server#80 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
xiaoyifang/goldendict-ng#3148 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 91/100
triton-inference-server/server#9069 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 4 giorni