Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Occasional JSClosure has been already released by Swift side #199

Copy link
Copy link
@vkhougaz-vifive

Description

@vkhougaz-vifive
Issue body actions

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
}
Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.