Skip to content
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

Data Persistence Issue with Data Storage Module in Multi-Module Setup Without Host Interaction #4019

Open
fxble opened this issue Jan 13, 2025 · 2 comments

Comments

@fxble
Copy link

fxble commented Jan 13, 2025

I am encountering an issue with WAMR where data stored in a module does not persist across function calls in a multi-module setup, without any host interaction.

Setup:

  • Entry Module: Host calls entry function, module manages interaction between submodules until finished.
  • Data Storage Submodule: Provides functions for storing and retrieving data to and from C arrays.
  • Other Submodules: import and call the storage module's get/set functions to read or write data to the arrays.

Problem:

  • Modules can call the storage module’s getter and setter functions successfully.
  • However, data does not persist between calls. The storage module resets to its initial state, as if a new instance is created for each function call.
  • Each data retrieval returns the initial values, rather than the values previously written.
  • I would guess the module instance gets deleted after every function call. (?)

Questions:

  1. Is this behavior expected in WAMR? Or do I probably have some kind of configuration error?
  2. Is there any way to achieve such a persistent data module - across multiple submodule interactions and without host intervention?

Environment:

  • bare-metal microcontroller (STM32, Cortex-M4)
  • WAMR 2.2.0 without WASI

Expected Behavior:
Data in the submodule should remain persistent and be accessible across all module interactions without reinitialization or host involvement.

Actual Behavior:
Data resets to the initial state following each function call.

BTW: I know I can implement a similar setup with host-controlled data/memory allocations, but in my experiment I deliberately don't want to do this.

@lum1n0us
Copy link
Collaborator

Provides functions for storing and retrieving data to and from C arrays.

Does this imply that the data is actually managed in the native/C world rather than within the WebAssembly world? If that's the case, I would recommend adding logs or setting gdb watchpoints to determine what is resetting the value.


I have some theories about what might be happening, but I would need to see the source code to be certain(wat + host embedding).

@fxble
Copy link
Author

fxble commented Jan 14, 2025

In general, I write all module code in C and use clang/wamrc to compile them to AOT files.


Does this imply that the data is actually managed in the native/C world rather than within the WebAssembly world?

From my understanding, the data is managed by the WebAssembly world, because I only create and access arrays inside the data module.


My implementation is as follows:

The native host code looks like this:

/* Host (main.c) */
wamr_init();
wasm_runtime_register_natives(...);
load_and_register_all_modules(...); // load modules as AOT byte array
wasm_module_inst_t entry_module_inst = wasm_runtime_instantiate(entry_module, ...);
wasm_exec_env_t entry_exec_env = wasm_runtime_create_exec_env(entry_module_inst, ...);
wasm_function_inst_t entry_func_inst = wasm_runtime_lookup_function(entry_module_inst, "entry");

wasm_runtime_call_wasm(entry_exec_env, entry_func_inst, 0, NULL)); // --> call the main modules entry function

The host calls the entry function of the main module. This entry function in turn calls imported functions from other modules:

/* Main Module (entry.c)*/
__attribute__((import_module("read")))
__attribute__((import_name("read_something")))
void read_something();

void entry() {
    read_something(); // --> imported function from submodule 1
    do_something(); // --> imported function from submodule 2
    write_something(); // --> imported function from submodule 3
}

These submodule functions need access to the array data. That data is managed by another submodule. Therefore, submodules 1, 2 and 3 import the get/set functions from the data storage module:

/* Submodule 1 (read.c) */
__attribute__((import_module("data")))
__attribute__((import_name("set_value")))
void set_value(int value, int index);

__attribute__((export_name("read_something")))
void read_something() {
    /* read something */
    set_value(8, 0); // --> imported function from data storage module
}

The data storage module is implemented as seen below:

/* Data Storage Module (data.c) */
int my_array[] = {1, 2, 3};

__attribute__((export_name("get_value")))
int get_value(int index) {
    if(index >= 0 && index < 3) {
        return array[index];
    }
    else {
        return INT_MAX;
    }
}

__attribute__((export_name("set_value")))
void set_value(int value, int index) {
    if(index >= 0 && index < 3) {
        array[index] = value;
    }
}

What I forgot to mention:
I checked the implementation of the data storage module by instantiating it and executing its get/set functions exclusively from the host and it worked. Because the host handles the instantiation and keeps the module instance alive, is that right?

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