Skip to content

Occasional JSClosure has been already released by Swift side #199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
vkhougaz-vifive opened this issue Jul 16, 2022 · 4 comments
Closed

Comments

@vkhougaz-vifive
Copy link

vkhougaz-vifive commented Jul 16, 2022

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
}
@kateinoigakukun
Copy link
Member

Thank you for detail report and repro. It looks like our runtime has something wrong. Let me check it

vkhougaz-vifive added a commit to vkhougaz-vifive/JavaScriptKit that referenced this issue Jul 18, 2022
@vkhougaz-vifive
Copy link
Author

I'm still running into this issue indexing a dict using a string passed in via JSValue

Is it possible that strings in wasm allocated (heap?) memory cannot be used to index dictionaries, but strings on the stack can? hmn.

@kateinoigakukun
Copy link
Member

kateinoigakukun commented Jul 31, 2022

OK, the root cause is that you are using command line model instead of reactor model. JavaScriptKit only supports reactor model. I've sent you a patch vkhougaz-vifive/swiftwasm-bug#1

We will add more friendly diagnostics to avoid this situation.
See also carton code: https://github.com/swiftwasm/carton/blob/b8cac7dc8e9c96e95406bfc5312768a2e659bd81/Sources/SwiftToolchain/Toolchain.swift#L368-L373

@vkhougaz-vifive
Copy link
Author

Okay, I don't know why I was having so many issues but I fixed the compilation command and it's working well. Thanks!

swift build --triple wasm32-unknown-wasi -c $MODE -Xswiftc -Xclang-linker -Xswiftc -mexec-model=reactor -Xlinker --export=main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants