Closed
Description
During development of an animated webassembly feature a function that takes and returns a large typed array started failing occasionally with an error similar to:
The JSClosure has been already released by Swift side. The closure is created at ...
This occurs approximately 50% of frames
I've tried to create a minimal reproduction here, details in the README:
https://github.com/vkhougaz-vifive/swiftwasm-bug
import Foundation
import JavaScriptKit
func reverseArray(bytes: [Float32]) -> [Float32] {
return [Float32](bytes.reversed())
}
let jsClosure = JSClosure { (input: [JSValue]) in
let bytes: [Float32] = try! JSValueDecoder().decode(from: input[0])
return reverseArray(bytes: bytes).jsValue
}
@_cdecl("main")
func main(_ i: Int32, _ j: Int32) -> Int32 {
JSObject.global.reverseFloat32Array = .object(jsClosure)
return 0
}
window.onload = async () => {
const output = document.getElementById("output")!;
function* generator() {
for (let step = 0; step < 10000; step++) {
yield Math.random();
}
}
await loadWasm(bugWasm);
function animate() {
try {
const bytes = Float32Array.from(generator());
const reversed = reverseFloat32Array(bytes);
output.innerHTML = reversed.join("\n");
} catch (e) {
console.error(e);
}
requestAnimationFrame(animate);
}
animate();
};
The hacky patch included there is... concerning, pointing towards either a dramatic misunderstanding of how swift works or a crazy low level memory issue.
/// Returns true if the host function has been already released, otherwise false.
@_cdecl("_call_host_function_impl")
func _call_host_function_impl(
_ hostFuncRef: JavaScriptHostFuncRef,
_ argv: UnsafePointer<RawJSValue>, _ argc: Int32,
_ callbackFuncRef: JavaScriptObjectRef
) -> Bool {
// TODO: This is some sort of horrible hack due to some sort of horrible wasm thing
// Otherwise the sharedClone SOMETIMES fails
let sharedClone = Dictionary(uniqueKeysWithValues: zip(JSClosure.sharedClosures.keys, JSClosure.sharedClosures.values))
guard let (_, hostFunc) = sharedClone[hostFuncRef] else {
return true
}
let arguments = UnsafeBufferPointer(start: argv, count: Int(argc)).map(\.jsValue)
let result = hostFunc(arguments)
let callbackFuncRef = JSFunction(id: callbackFuncRef)
_ = callbackFuncRef(result)
return false
}
Metadata
Metadata
Assignees
Labels
No labels