Crashes on WebGL with Cloud Code calls (_WebSocketFree)

Hello there !

We are currently working on a WebGL game that relies a lot on Cloud Code.

On WebGL, we have been systematically experiencing the same crash in the authentication steps of our game :

Uncaught TypeError: Cannot read properties of undefined (reading 'readyState')
    at _WebSocketFree (test_lobah.framework.js:8088:45)
    at invoke_vi (test_lobah.framework.js:18587:29)
    at WebSocketFactory_HandleInstanceDestroy_mB533D21CA7D36EDF0D7D4FD279948195E8A39534 (test_lobah.wasm:0x3570a2c)
    at invoke_vii (test_lobah.framework.js:18576:29)
    at WebSocket_Finalize_m4CCA49F0C6CB66F0B3435546A16A14424F53E429 (test_lobah.wasm:0x357069f)
    at RuntimeInvoker_TrueVoid_t4861ACF8F4594C3437BB48B6E56783494B843915(:61754/void (*)(), MethodInfo const*, void*, void**, void*) (http://localhost:61754/Build/test_lobah.wasm)
    at il2cpp::vm::Runtime::InvokeWithThrow(:61754/MethodInfo const*, void*, void**) (http://localhost:61754/Build/test_lobah.wasm)
    at invoke_iiii (test_lobah.framework.js:18631:36)
    at il2cpp::vm::Runtime::Invoke(:61754/MethodInfo const*, void*, void**, Il2CppException**) (http://localhost:61754/Build/test_lobah.wasm)
    at il2cpp::gc::GarbageCollector::RunFinalizer(:61754/void*, void*) (http://localhost:61754/Build/test_lobah.wasm)
    at GC_invoke_finalizers (test_lobah.wasm:0x9a223)
    at il2cpp::gc::GarbageCollector::CollectALittle(:61754/) (http://localhost:61754/Build/test_lobah.wasm)
    at il2cpp_gc_collect_a_little (test_lobah.wasm:0xee475)
    at scripting_gc_collect_a_little(:61754/) (http://localhost:61754/Build/test_lobah.wasm)
    at MainLoop(:61754/) (http://localhost:61754/Build/test_lobah.wasm)
    at _JS_CallAsLongAsNoExceptionsSeen (test_lobah.framework.js:1952:28)
    at UpdateUnityFrame(:61754/) (http://localhost:61754/Build/test_lobah.wasm)
    at callUserCallback (test_lobah.framework.js:5539:9)
    at Object.runIter (test_lobah.framework.js:5595:11)
    at Browser_mainLoop_runner (test_lobah.framework.js:6233:26)

After some investigation, we managed to isolate the issue. This happens after calling one of our cloud code modules.

We looked into the generated js code and enabled debugging for this WebSocket API, to get some logs.

What we believe happens is that Cloud Code opens a WebSocket, then closes the WebSocket when it’s done.
After a short time, the WebSocket is garbage collected and _WebSocketFree is called. This free function checks if the web socket is closed before deleting it (for safety reasons I suppose, if for whatever reason it wasn’t closed before being garbage collected or freed).

function _WebSocketFree(instanceId) {
  
  		var instance = webSocketState.instances[instanceId];
  
  		if (!instance) return 0;

      console.log(`[JSLIB WebSocket] ${JSON.stringify(instance)}`);
  
  		// Close if not closed
  		if (instance.ws !== null && instance.ws.readyState < 2)
  			instance.ws.close();
  
  		// Remove reference
  		delete webSocketState.instances[instanceId];
  
  		return 0;
  
  	}

When we log the content of the instance, we find out that the actual websocket (instance.ws) is undefined (which makes sense, since the web socket was already explicitly closed). This seems to be the cause of the crash - _WebSocketFree checks instance.ws for null, but doesn’t check if it’s undefined.

Our hotfix, until this gets properly resolved in a future update :

function _WebSocketFree(instanceId) {
  
  		var instance = webSocketState.instances[instanceId];
  
  		if (!instance) return 0;

      console.log(`[JSLIB WebSocket] ${JSON.stringify(instance)}`);
  
  		// Close if not closed
  		if (instance.ws !== null && instance.ws !== undefined && instance.ws.readyState < 2)
  			instance.ws.close();
  
  		// Remove reference
  		delete webSocketState.instances[instanceId];
  
  		return 0;
  
  	}

Now, messing with the generated js code is not ideal and we are not aware of all potential side effects.

Also, this forces us to apply the fix manually after every build.

We are launching our game in a couple weeks, so I guess we will have to stick to that solution for the time being. It would be great if this fix could be shipped in the next update for the relevant package.

PS : enabling WebAssembly tables is also an issue with that bit in particular, since there are functions related to that WebSocket conundrum that seem to still be called via the legacy Emscripten dynCall.

To make it work with WebAssembly tables enabled, we applied another fix that is explained in that thread (remapping via a .jspre plugin) : makeDynCall replacing dynCall_ in Unity 6?

1 Like

I think you can create a JSLIB file of your own, and have there a method named WebSocketFree.
That should override the generated _WebSocketFree.
And then you won’t need to manually override this method on every build.

I’m not sure if it’s a method from a package or the editor. If it’s in a package, you can duplicate the package and override it in the source.

If it’s from the editor, you can override it in the editor web files, but then you’ll have to do that in every machine you use to compile (will be difficult on cloud builds).

I think that first option is the cleanest.

Hello,

The respective team is currently working on a fix for the WebSocket ws undefined unhandled exception and it will be included in the upcoming release.

Please let me know if there are any questions!

2 Likes