Stream: git-wasmtime

Topic: wasmtime / issue #10521 mutable data & memory access in w...


view this post on Zulip Wasmtime GitHub notifications bot (Apr 03 2025 at 18:45):

rnxpyke opened issue #10521:

Feature

Currently it's not possible to have a mutable reference to custom store data and something like store memory at the same time.
E.g, the following doesn't compile:

fn hostfunc(mut caller: Caller<'_, CustomData>, (ptr, len): (i32, i32)) -> Box<dyn Future<Output = anyhow::Result<i32>> + Send + '_> {
    let buf: &mut Vec<u8> = caller.data_mut().buf;
    let Some(Extern::Memory(m)) = caller.get_export("memory") else { panic!() };
    m.read(&caller, ptr as usize, buf)?;
    return Ok(0);
}

By changing the struct layout of Caller<'_, CustomData> into something like

pub struct Context<'a, T> {
    pub caller: Caller<'a> // caller without data
    pub data: &'a mut T,
}

you can access the caller and data fields seperately.
The compiler is smart enough to allow mutable references to disjoint struct fields.

Benefit

code like this would typecheck:

fn hostfunc(mut ctx: Context<'_, CustomData>, (ptr, len): (i32, i32)) -> Box<dyn Future<Output = anyhow::Result<i32>> + Send + '_> {
    let buf: &mut Vec<u8> = ctx.data.buf;
    let Some(Extern::Memory(m)) = caller.get_export("memory") else { panic!() };
    m.read(&ctx.caller, ptr as usize, buf)?;
    return Ok(0);
}

when copying data to or especially from wasm memory, you can skip an additional allocation that would otherwise be necessary.

Implementation

Expose data and the wasm Caller / Store as seperate (public) struct fields.

Alternatives

Current workarounds to the concrete problem of copying memory would be

both solutions have drawbacks.
either you introduce another allocation, or you give up on some type safety by unwrapping options.

view this post on Zulip Wasmtime GitHub notifications bot (Apr 03 2025 at 18:58):

alexcrichton commented on issue #10521:

Can you use Memory::data_and_store_mut to solve your use case here?

view this post on Zulip Wasmtime GitHub notifications bot (Apr 05 2025 at 11:29):

rnxpyke commented on issue #10521:

hm, I didn't see that method when speed-reading the documentation.
I think this api would also solve my allocation issues. :+1:

It's kinda confusing that in the context of a host function using the Caller struct, data refers to the generic type parameter T, but in the Memory struct it's swapped, data refers to raw bytes and store to the generic type.

view this post on Zulip Wasmtime GitHub notifications bot (Apr 07 2025 at 14:38):

alexcrichton commented on issue #10521:

It's true it's unfortunate, but we're only so good at naming...

Given that it works for you though I'm going to close this, but if you have anything further feel free to post here as well

view this post on Zulip Wasmtime GitHub notifications bot (Apr 07 2025 at 14:38):

alexcrichton closed issue #10521:

Feature

Currently it's not possible to have a mutable reference to custom store data and something like store memory at the same time.
E.g, the following doesn't compile:

fn hostfunc(mut caller: Caller<'_, CustomData>, (ptr, len): (i32, i32)) -> Box<dyn Future<Output = anyhow::Result<i32>> + Send + '_> {
    let buf: &mut Vec<u8> = caller.data_mut().buf;
    let Some(Extern::Memory(m)) = caller.get_export("memory") else { panic!() };
    m.read(&caller, ptr as usize, buf)?;
    return Ok(0);
}

By changing the struct layout of Caller<'_, CustomData> into something like

pub struct Context<'a, T> {
    pub caller: Caller<'a> // caller without data
    pub data: &'a mut T,
}

you can access the caller and data fields seperately.
The compiler is smart enough to allow mutable references to disjoint struct fields.

Benefit

code like this would typecheck:

fn hostfunc(mut ctx: Context<'_, CustomData>, (ptr, len): (i32, i32)) -> Box<dyn Future<Output = anyhow::Result<i32>> + Send + '_> {
    let buf: &mut Vec<u8> = ctx.data.buf;
    let Some(Extern::Memory(m)) = caller.get_export("memory") else { panic!() };
    m.read(&ctx.caller, ptr as usize, buf)?;
    return Ok(0);
}

when copying data to or especially from wasm memory, you can skip an additional allocation that would otherwise be necessary.

Implementation

Expose data and the wasm Caller / Store as seperate (public) struct fields.

Alternatives

Current workarounds to the concrete problem of copying memory would be

both solutions have drawbacks.
either you introduce another allocation, or you give up on some type safety by unwrapping options.


Last updated: Apr 17 2025 at 10:03 UTC