From ee22734c69cccf634771627f461f79e8ee8d359f Mon Sep 17 00:00:00 2001 From: strandfield Date: Wed, 5 Nov 2025 15:58:24 +0100 Subject: [PATCH 1/3] add test case for emscripten_websocket_deinitialize() The test currently fails as emscripten_websocket_deinitialize() is currently broken. --- test/test_sockets.py | 1 + test/websocket/test_websocket_send.c | 34 +++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/test/test_sockets.py b/test/test_sockets.py index 1a21d5866bdca..0e94c67124bda 100644 --- a/test/test_sockets.py +++ b/test/test_sockets.py @@ -388,6 +388,7 @@ def test_nodejs_sockets_echo_subprotocol_runtime(self): @parameterized({ '': [[]], 'shared': [['-sSHARED_MEMORY']], + 'deinitialize': [['-DTEST_EMSCRIPTEN_WEBSOCKET_DEINITIALIZE']], }) @requires_dev_dependency('ws') def test_websocket_send(self, args): diff --git a/test/websocket/test_websocket_send.c b/test/websocket/test_websocket_send.c index a3a01c1d3b695..86e9eb701ddd8 100644 --- a/test/websocket/test_websocket_send.c +++ b/test/websocket/test_websocket_send.c @@ -3,8 +3,11 @@ #include #include -// This test performs that same server communications using two different +// This test performs the same server communications using two different // sockets. This verifies that multiple sockets are supported simultaneously. +// Depending on whether TEST_EMSCRIPTEN_WEBSOCKET_DEINITIALIZE is defined, +// cleanup is either performed using emscripten_websocket_deinitialize() or +// emscripten_websocket_close() and emscripten_websocket_delete(). EMSCRIPTEN_WEBSOCKET_T sock1; EMSCRIPTEN_WEBSOCKET_T sock2; @@ -44,6 +47,7 @@ bool WebSocketError(int eventType, const EmscriptenWebSocketErrorEvent *e, void bool WebSocketMessage(int eventType, const EmscriptenWebSocketMessageEvent *e, void *userData) { printf("message(socket=%d, eventType=%d, userData=%p data=%p, numBytes=%d, isText=%d)\n", e->socket, eventType, userData, e->data, e->numBytes, e->isText); static int text_received = 0; + static int binary_received = 0; assert(e->socket == sock1 || e->socket == sock2); if (e->isText) { printf("text data: \"%s\"\n", e->data); @@ -54,13 +58,41 @@ bool WebSocketMessage(int eventType, const EmscriptenWebSocketMessageEvent *e, v // We expect to receive the text message before the binary one assert(text_received); + binary_received++; printf("binary data:"); for (int i = 0; i < e->numBytes; ++i) { printf(" %02X", e->data[i]); assert(e->data[i] == i); } printf("\n"); + +#ifndef TEST_EMSCRIPTEN_WEBSOCKET_DEINITIALIZE emscripten_websocket_close(e->socket, 0, 0); + // The WebSocket is being closed, but its handle is still valid. + // It should therefore still be possible to query its state. + unsigned short ready_state; + EMSCRIPTEN_RESULT result = emscripten_websocket_get_ready_state(e->socket, &ready_state); + assert(result == EMSCRIPTEN_RESULT_SUCCESS); + // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState + assert(ready_state == 2); // 2 = CLOSING +#else + if (binary_received == 2) { + // We successfully received binary data from both websockets. + // We are done. We can deinitialize and exit. + emscripten_websocket_deinitialize(); + // All websocket handles are invalidated. + // It is no longer possible to query their state. + unsigned short ready_state; + EMSCRIPTEN_RESULT result = emscripten_websocket_get_ready_state(e->socket, &ready_state); + // Note: the following assert() currently fails because + // emscripten_websocket_deinitialize() is broken. + assert(result == EMSCRIPTEN_RESULT_INVALID_TARGET); + (void)ready_state; + sock1 = sock2 = 0; + emscripten_force_exit(0); + } +#endif // TEST_EMSCRIPTEN_WEBSOCKET_DEINITIALIZE + return 0; } From 719517316f14bba502bb452f84f9907396e9e68e Mon Sep 17 00:00:00 2001 From: strandfield Date: Wed, 5 Nov 2025 16:14:56 +0100 Subject: [PATCH 2/3] fix emscripten_websocket_deinitialize() --- src/lib/libwebsocket.js | 10 ++++------ test/websocket/test_websocket_send.c | 2 -- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/lib/libwebsocket.js b/src/lib/libwebsocket.js index 3735d78e8a8d9..55e50cb1f12e4 100644 --- a/src/lib/libwebsocket.js +++ b/src/lib/libwebsocket.js @@ -409,21 +409,19 @@ var LibraryWebSocket = { emscripten_websocket_is_supported__proxy: 'sync', emscripten_websocket_is_supported: () => typeof WebSocket != 'undefined', - emscripten_websocket_deinitialize__deps: ['$WS'], + emscripten_websocket_deinitialize__deps: ['$webSockets', 'emscripten_websocket_delete'], emscripten_websocket_deinitialize__proxy: 'sync', - emscripten_websocket_deinitialize__deps: ['emscripten_websocket_delete'], emscripten_websocket_deinitialize: () => { #if WEBSOCKET_DEBUG dbg('emscripten_websocket_deinitialize()'); #endif - for (var i in WS.sockets) { - var socket = WS.sockets[i]; - if (socket) { + for (var i in webSockets.allocated) { + if (webSockets.has(i)) { + var socket = webSockets.get(i); socket.close(); _emscripten_websocket_delete(i); } } - WS.sockets = []; } } diff --git a/test/websocket/test_websocket_send.c b/test/websocket/test_websocket_send.c index 86e9eb701ddd8..260f796a6dec6 100644 --- a/test/websocket/test_websocket_send.c +++ b/test/websocket/test_websocket_send.c @@ -84,8 +84,6 @@ bool WebSocketMessage(int eventType, const EmscriptenWebSocketMessageEvent *e, v // It is no longer possible to query their state. unsigned short ready_state; EMSCRIPTEN_RESULT result = emscripten_websocket_get_ready_state(e->socket, &ready_state); - // Note: the following assert() currently fails because - // emscripten_websocket_deinitialize() is broken. assert(result == EMSCRIPTEN_RESULT_INVALID_TARGET); (void)ready_state; sock1 = sock2 = 0; From 14dec526dc2f517c5581999978f7a059ebdfd8f3 Mon Sep 17 00:00:00 2001 From: strandfield Date: Fri, 7 Nov 2025 11:31:46 +0100 Subject: [PATCH 3/3] Add `list()` function to `HandleAllocator` for listing valid ids Since it is possible to build such a list by manually iterating over the `allocated` array, I think it is better to provide a function that does just that. Accessing the `allocated` member from outside the class should be avoided as it is more an implementation detail than a part of the class interface. --- src/lib/libcore.js | 9 +++++++++ src/lib/libwebsocket.js | 10 ++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lib/libcore.js b/src/lib/libcore.js index 64d153ebbd826..41ffb2a36e24a 100644 --- a/src/lib/libcore.js +++ b/src/lib/libcore.js @@ -2264,6 +2264,15 @@ addToLibrary({ this.allocated[id] = undefined; this.freelist.push(id); } + list() { + var valid_ids = []; + for (var i = 1; i < this.allocated.length; i++) { + if (this.allocated[i] !== undefined) { + valid_ids.push(i); + } + } + return valid_ids; + } }, $wasmTable__docs: '/** @type {WebAssembly.Table} */', diff --git a/src/lib/libwebsocket.js b/src/lib/libwebsocket.js index 55e50cb1f12e4..793c034d1cbe0 100644 --- a/src/lib/libwebsocket.js +++ b/src/lib/libwebsocket.js @@ -415,12 +415,10 @@ var LibraryWebSocket = { #if WEBSOCKET_DEBUG dbg('emscripten_websocket_deinitialize()'); #endif - for (var i in webSockets.allocated) { - if (webSockets.has(i)) { - var socket = webSockets.get(i); - socket.close(); - _emscripten_websocket_delete(i); - } + for (let id of webSockets.list()) { + let socket = webSockets.get(id); + socket.close(); + _emscripten_websocket_delete(id); } } }