Stdweb:: js! macro, memory leak on callback

Quoting: https://docs.rs/stdweb/0.4.20/stdweb/macro.js.html

Any callbacks passed into JavaScript will leak memory by default! You need to call .drop() on the callback from the JavaScript side to free it.

Can anyone explain how/why this happens? (And what the fix is.)

It happens because the JS side doesn't know if the wasm code is still using it, so it can't free it. If the closure stays in Rust, it will be dropped at the end of the scope as normal, but when passed to Javascript, you will need to manually drop it.

1 Like

@alice : I don't understand the following: Why is it that (1) the above argument works for Callbacks, but (2) does not hold for Objects ?

When I'm looking at the js! examples, when we pass a Object from rust to js, all it uses is js! { ... @{ ... obj .. } ... }

  1. rust/wasm32 does not know if js is still using Callback/Object
  2. therefore, js needs to manually call .drop on Callback/Object

Why is it that the above is true for Callback but unnecessary for Object? There is something fundamental here that I'm not understanding.

I don't actually know, I'm just going by the documentation. I'd guess that the difference is that the callbacks contain some wasm code while the objects do not?

bumping post

Can anyone else enlighten on difference of callbacks vs objects that results in needing to manually drop callbacks but not objects?

Objects sent to JavaScript via stdweb::js! are generally fully transformed into JS values - and retain no reference to the Rust side. This is done either manually, or via serde serialization to json.

The exceptions - mainly UnsafeTypedArray - explicitly note their behavior in the rust type documentation rather than in js! documentation.

Edit: apologies for the necropost. Saw the latest and didn't realize it was 2 months ago.

2 Likes

@daboross : This is insightful information. Thanks for the post! I still haven't quite figured this issue out yet. Let me see if I understand your argument:

  1. Inside js!, rust OBJECTS are converted to JS Values. They have no reference to Rust side, no need to call drop.

  2. Inside js!, a Rust callback/closure can NOT be converted into a JS Value (because the function's 'code' is in wasm, not pure JS). This closure/callback might hold Rc's / other resources in Rust land.

  3. To free up the Rc's / other resources / actual code of the callback, we need to manually call drop on it.

===

Is the above an accurate statement of your argument?

1 Like

Yep! Looks accurate to me.

1 Like

@daboross: Happy cake day! Glad you're part of the Rust community.

1 Like

Thanks! Glad you're here too.