Assertion Failed: pSock->m_callback.m_fnCallback (socketthread.cpp:2609) after re-initializing the library with GameNetworkingSockets_Kill + GameNetworkingSockets_Init
Maintainer thường phản hồi trong vòng 2 ngày
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 45/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- cpp
- Lĩnh vực
- networking
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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.
- Ngôn ngữ chính
- C++
- Star
- 10k
- Fork
- 767
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của ValveSoftware/GameNetworkingSockets
-
ICE (native client): relay-only connections time out against a real TURN server; host-socket checks starve the relay pairsCó thể đã có người làm @Mr444-dev đã nhận 4 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 79/100
ValveSoftware/GameNetworkingSockets#452 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 4/5 Hơn một tuần Mức phù hợp với người mới 12/100
ValveSoftware/GameNetworkingSockets#465 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 18/100
ValveSoftware/GameNetworkingSockets#464 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
ISteamNetworkingSockets: Trying to use over 8 network lanes/channels failsCó thể đã có người làm @jpggvilaca đã nhận 14 ngày trước. Đang mở
Độ khó 4/5 1-2 ngày Mức phù hợp với người mới 55/100
ValveSoftware/GameNetworkingSockets#455 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
ConfigureConnectionLanes: reconfiguring lanes can leave one lane in a stale priority classCó thể đã có người làm @sebxt đã nhận 18 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 35/100
ValveSoftware/GameNetworkingSockets#453 ·
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của ValveSoftware/GameNetworkingSockets
Issue tương tự
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Sunrisepeak/mcpp-language-server#80 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
#2754 make deepseek api not workĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
xiaoyifang/goldendict-ng#3148 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 91/100
triton-inference-server/server#9069 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 4 ngày