Wasmer: Environment passed to Function::new_native_with_env is never dropped and leaks memory

Created on 2 Oct 2020  路  3Comments  路  Source: wasmerio/wasmer

I'm running wasmer 1.0.0-alpha.3 and recently run into a problem that I'm not sure how to work around. For every instance I crate a new ImportObject capturing an environment with some functions.

The issue is that this environment is never dropped and I can't figure out a way of releasing the memory. My environment is a bit heavier and after running the app for some time I start running out of memory.

I created a minimal example just to illustrate the issue:

use wasmer::{Store, Instance, Module, Exports, ImportObject, Function};

struct Env {
    multiplier: i32,
}

impl Drop for Env {
    fn drop(&mut self) {
        println!("Dropping: {}", self.multiplier);
    }
}

fn main() {
    let store = Store::default();
    let module = Module::new(&store, "(module (import \"env\" \"sum_and_multiply\" (func (param i32 i32) (result i32))))").unwrap();

    let env_dropped = Env { multiplier: 1 };
    let env_never_dropped = Env { multiplier: 2 };
    let f = Function::new_native_with_env(&store, env_never_dropped, sum_and_multiply);

    let mut exports = Exports::new();
    exports.insert("sum_and_multiply", f);

    let mut import_object = ImportObject::new();
    import_object.register("env", exports);

    let instance = Instance::new(&module, &import_object).unwrap();
}

fn sum_and_multiply(env: &mut Env, a: i32, b: i32) -> i32 {
    (a + b) * env.multiplier
}

The output of this app is:

Dropping: 1

As you can see env_dropped is never moved and drops, but env_never_dropped doesn't get it's drop method called. Even after the instance, module and store are all gone.

Ideally I would expect that once there are no more Instances referencing the ImportObject that the resources get released.

It can also be that I structured my app in an unconventional way. Is there maybe a better approach to this?

馃悶 bug

All 3 comments

I think this is related to #1584.

Thanks for the issue, I think this is the same as #1584 too;

I believe once either #1663 or #1625 lands this will be easier to fix as the general ownership of Env is clarified a bit more in those PRs.

We can probably make a solution that's compatible with both of those PRs though

Closing this as #1865 got merged and it fixes the issue. Here is the original snippet, adapted with new requirements:

use wasmer::{Store, Instance, Module, Exports, ImportObject, Function, WasmerEnv};

#[derive(WasmerEnv, Clone)]
struct Env {
    multiplier: i32,
}

impl Drop for Env {
    fn drop(&mut self) {
        println!("Dropping: {:?}", self.multiplier);
    }
}

fn main() {
    let store = Store::default();
    let module = Module::new(&store, "(module (import \"env\" \"sum_and_multiply\" (func (param i32 i32) (result i32))))").unwrap();

    let env_dropped = Env { multiplier: 1 };
    let env_never_dropped = Env { multiplier: 2 };

    let f = Function::new_native_with_env(&store, env_never_dropped, sum_and_multiply);

    let mut exports = Exports::new();
    exports.insert("sum_and_multiply", f);

    let mut import_object = ImportObject::new();
    import_object.register("env", exports);

    let instance = Instance::new(&module, &import_object).unwrap();
}

fn sum_and_multiply(env: &Env, a: i32, b: i32) -> i32 {
    (a + b) * env.multiplier
}

The output is now:

Dropping: 2
Dropping: 2
Dropping: 1

What did I change in the snippet:

  • Env now has to derive WasmerEnv and Clone
  • the host function now receives a &Env instead of a &mut Env (see https://github.com/wasmerio/wasmer/blob/master/examples/imports_function_env.rs to learn how to allow Env mutability)
Was this page helpful?
0 / 5 - 0 ratings