Stream: git-wasmtime

Topic: wasmtime / issue #10333 question: How to ensure c-api thr...


view this post on Zulip Wasmtime GitHub notifications bot (Mar 05 2025 at 08:02):

liupeidong0620 opened issue #10333:

question

  1. I created multiple wasm_wasmtime_plugin_t
  2. Each thread runs a wasm_wasmtime_plugin_t
  3. Is this thread safe?
  4. The wasmtime_func_call function calls the malloc function exported by wasm. Is this thread safe?
typedef struct { // Load the same wasm program and create multiple wasm_wasmtime_plugin_t instances
    wasm_engine_t           *vm_engine;
    wasmtime_module_t       *module;
    wasmtime_store_t        *store;
    wasmtime_context_t      *context;
    wasmtime_linker_t       *linker;
    wasmtime_instance_t      instance;
    wasmtime_memory_t        memory;
} wasm_wasmtime_plugin_t;

int main() {
....
wasm_wasmtime_plugin_t plugins[6];
// Load the same wasm program and create multiple wasm_wasmtime_plugin_t instances

for (int i = 0; i < 6; i ++) {
std::thread t([plugin_num = i] {
       //  Is it thread safe?
        error = wasmtime_func_call(plugins[plugin_num]->context, &func.of.func, params, param_num, results,
                               has_result ? 1 : 0, &trap);
    });
}
// wait thread ok
....


}

code (wasm_wasmtime_plugin_t initialization process, specific code)

// wasmtime_vm.h

#pragma once

#include <wasi.h>
#include <wasm.h>
#include <wasmtime.h>

#include "wasm_base.h"
#include "wasm_api.h"

#include <string>

typedef struct {
    wasm_engine_t           *vm_engine;
    wasmtime_module_t       *module;
    wasmtime_store_t        *store;
    wasmtime_context_t      *context;
    wasmtime_linker_t       *linker;
    wasmtime_instance_t      instance;
    wasmtime_memory_t        memory;
} wasm_wasmtime_plugin_t;

typedef struct {
    std::string                name;
    wasmtime_func_callback_t cb;
    int8_t                   param_num;
    wasm_valkind_t           param_type[MAX_WASM_API_ARG];
} wasm_wasmtime_host_api_t;

class WasmtimeVM : public WasmBaseVM {
public:
    WasmtimeVM() {}
    virtual int load(std::string& wasm_file, std::string& wasm_name); // load wasm and init
    virtual int32_t wasm_call(std::string func_name, bool has_result, int param_type, ...); // call wasm func

    virtual int32_t wasm_memory_alloc(int32_t size);
    virtual char* wasm_get_memory(int32_t addr, int32_t size);

    virtual ~WasmtimeVM();


    virtual void setCtxId(int32_t wasm_ctx_id) {
        wasm_ctx_id_ = wasm_ctx_id;
    }
    virtual int32_t getCtxId() {
        return wasm_ctx_id_;
    }
    virtual std::string& get_wasm_name() {
        return wasm_name_;
    }
    virtual int32_t get_wasm_id() {
        return id_;
    }
    virtual void set_wasm_id(int32_t id) {
        id_ = id;
    }
private:
    wasm_functype_t *wasmtime_host_api_func(const wasm_wasmtime_host_api_t *api);
    void wasmtime_report_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
    bool wasm_wasmtime_has(std::string& name);

    wasm_wasmtime_plugin_t* plugin;
    int32_t wasm_ctx_id_ = 0;
    std::string wasm_name_;
    int32_t id_;
};

// wasmtime_vm.cc

#include "wasmtime_vm.h"
#include <iostream>

#define DEFINE_WASM_API(NAME, ARG_CHECK) \
    static wasm_trap_t* wasmtime_##NAME( \
        void *env, \
        wasmtime_caller_t *caller, \
        const wasmtime_val_t *args, \
        size_t nargs, \
        wasmtime_val_t *results, \
        size_t nresults \
    ) { \
        ARG_CHECK \
        results[0].kind = WASMTIME_I32; \
        results[0].of.i32 = res; \
        return NULL; \
    }
#define DEFINE_WASM_NAME(NAME, ARG) \
    {#NAME, wasmtime_##NAME, ARG},

#define DEFINE_WASM_NAME_ARG_VOID \
    0, {}
#define DEFINE_WASM_API_ARG_CHECK_VOID(NAME) \
    int32_t res = NAME();

#define DEFINE_WASM_NAME_ARG_I32_1 \
    1, { \
    WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_1(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t res = NAME(p0);

#define DEFINE_WASM_NAME_ARG_I32_2 \
    2, { \
    WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_2(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t res = NAME(p0, p1);

#define DEFINE_WASM_NAME_ARG_I32_3 \
    3, { \
    WASM_I32, WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_3(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t res = NAME(p0, p1, p2);

#define DEFINE_WASM_NAME_ARG_I32_4 \
    4, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_4(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3);

#define DEFINE_WASM_NAME_ARG_I32_5 \
    5, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
}
#define DEFINE_WASM_API_ARG_CHECK_I32_5(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4);

#define DEFINE_WASM_NAME_ARG_I32_6 \
    6, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
    WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_6(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t p5 = args[5].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4, p5);

#define DEFINE_WASM_NAME_ARG_I32_7 \
    7, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
    WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_7(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t p5 = args[5].of.i32; \
    int32_t p6 = args[6].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4, p5, p6);

DEFINE_WASM_API(tendis_get_buffer_bytes,
                DEFINE_WASM_API_ARG_CHECK_I32_4(tendis_get_buffer_bytes))

DEFINE_WASM_API(tendis_set_buffer_bytes,
                DEFINE_WASM_API_ARG_CHECK_I32_4(tendis_set_buffer_bytes))


static wasm_wasmtime_host_api_t host_apis[] = {
    DEFINE_WASM_NAME(tendis_get_buffer_bytes, DEFINE_WASM_NAME_ARG_I32_4)
    DEFINE_WASM_NAME(tendis_set_buffer_bytes, DEFINE_WASM_NAME_ARG_I32_4)
    { "", NULL, 0, {} }
};

static wasmtime_val_t   param_int32[1] = {{ .kind = WASMTIME_I32 }};
static wasmtime_val_t   param_int32_int32[2] = {{ .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }};
static wasmtime_val_t   param_int32_int32_int32[3] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
};
static wasmtime_val_t   param_int32_int32_int32_int32[4] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
    { .kind = WASMTIME_I32 },
};
static wasmtime_val_t   param_int32_int32_int32_int32_int32[5] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
};

wasm_functype_t *
WasmtimeVM::wasmtime_host_api_func(const wasm_wasmtime_host_api_t *api)
{
    int                i;
    wasm_valtype_vec_t param_vec, result_vec;
    wasm_valtype_t    *param[MAX_WASM_API_ARG];
    wasm_valtype_t    *result[1];
    wasm_functype_t   *f;

    for (i = 0; i < api->param_num; i++) {
        param[i] = wasm_valtype_new(api->param_type[i]);
    }

    result[0] = wasm_valtype_new(WASM_I32);
    wasm_valtype_vec_new(&param_vec, api->param_num, param);
    wasm_valtype_vec_new(&result_vec, 1, result);

    f = wasm_functype_new(&param_vec, &result_vec);
    return f;
}

void
WasmtimeVM::wasmtime_report_error(const char *message,
    wasmtime_error_t *error, wasm_trap_t *trap)
{
    wasm_byte_vec_t error_message;

    if (error != NULL) {
        wasmtime_error_message(error, &error_message);
        wasmtime_error_delete(error);
    } else {
        wasm_trap_message(trap, &error_message);
        wasm_trap_delete(trap);
    }
    std::cout << message << std::endl;
    std::cout.write(error_message.data, error_message.size);
    std::cout << std::endl;
    wasm_byte_vec_delete(&error_message);
}

int WasmtimeVM::load(std::string& bytecode, std::string& wasm_name) {
    size_t                        i;
    bool                          ok;
    wasm_engine_t                *vm_engine;
    wasm_trap_t                  *trap = NULL;
    wasmtime_module_t            *module;
    wasmtime_store_t             *store;
    wasmtime_context_t           *context;
    wasmtime_linker_t            *linker;
    wasi_config_t                *wasi_config;
    wasmtime_error_t             *error;
    wasmtime_extern_t             item;

    plugin = new wasm_wasmtime_plugin_t;
    wasm_name_ = wasm_name;

    vm_engine = wasm_engine_new();
    if (vm_engine == NULL) {
        std::cout << "failed to new engine" << std::endl;
        return -1;
    }
    //wasmtime_module_deserialize();
    error = wasmtime_module_new(vm_engine, (const uint8_t*) bytecode.c_str(), bytecode.size(), &module);
    //error = wasmtime_module_deserialize(vm_engine, (const uint8_t*) bytecode.c_str(), bytecode.size(), &module);
    if (error != NULL) {
        wasmtime_report_error("failed to new module: ", error, NULL);
        return -2;
    }

    store = wasmtime_store_new(vm_engine, NULL, NULL);
    if (store == NULL) {
        return -3;
    }

    context = wasmtime_store_context(store);

    wasi_config = wasi_config_new();
    if (wasi_config == NULL) {
        return -4;
    }

    wasi_config_inherit_env(wasi_config);
    wasi_config_inherit_stdin(wasi_config);
    wasi_config_inherit_stdout(wasi_config);
    wasi_config_inherit_stderr(wasi_config);

    error = wasmtime_context_set_wasi(context, wasi_config);
    if (error != NULL) {
        wasmtime_report_error("failed to init
[message truncated]

view this post on Zulip Wasmtime GitHub notifications bot (Mar 05 2025 at 10:06):

liupeidong0620 edited issue #10333:

question

  1. I created multiple wasm_wasmtime_plugin_t
  2. Each thread runs a wasm_wasmtime_plugin_t
  3. Is this thread safe?
  4. The wasmtime_func_call function calls the malloc function exported by wasm. Is this thread safe?
typedef struct { // Load the same wasm program and create multiple wasm_wasmtime_plugin_t instances
    wasm_engine_t           *vm_engine;
    wasmtime_module_t       *module;
    wasmtime_store_t        *store;
    wasmtime_context_t      *context;
    wasmtime_linker_t       *linker;
    wasmtime_instance_t      instance;
    wasmtime_memory_t        memory;
} wasm_wasmtime_plugin_t;

int main() {
....
wasm_wasmtime_plugin_t plugins[6];
// Load the same wasm program and create multiple wasm_wasmtime_plugin_t instances

for (int i = 0; i < 6; i ++) {
std::thread t([plugin_num = i] {
       //  Is it thread safe?
        error = wasmtime_func_call(plugins[plugin_num]->context, &func.of.func, params, param_num, results,
                               has_result ? 1 : 0, &trap);
    });
}
// wait thread ok
....


}

code (wasm_wasmtime_plugin_t initialization process, specific code)

// wasmtime_vm.h

#pragma once

#include <wasi.h>
#include <wasm.h>
#include <wasmtime.h>

#include "wasm_base.h"
#include "wasm_api.h"

#include <string>

typedef struct {
    wasm_engine_t           *vm_engine;
    wasmtime_module_t       *module;
    wasmtime_store_t        *store;
    wasmtime_context_t      *context;
    wasmtime_linker_t       *linker;
    wasmtime_instance_t      instance;
    wasmtime_memory_t        memory;
} wasm_wasmtime_plugin_t;

typedef struct {
    std::string                name;
    wasmtime_func_callback_t cb;
    int8_t                   param_num;
    wasm_valkind_t           param_type[MAX_WASM_API_ARG];
} wasm_wasmtime_host_api_t;

class WasmtimeVM : public WasmBaseVM {
public:
    WasmtimeVM() {}
    virtual int load(std::string& wasm_file, std::string& wasm_name); // load wasm and init
    virtual int32_t wasm_call(std::string func_name, bool has_result, int param_type, ...); // call wasm func

    virtual int32_t wasm_memory_alloc(int32_t size);
    virtual char* wasm_get_memory(int32_t addr, int32_t size);

    virtual ~WasmtimeVM();


    virtual void setCtxId(int32_t wasm_ctx_id) {
        wasm_ctx_id_ = wasm_ctx_id;
    }
    virtual int32_t getCtxId() {
        return wasm_ctx_id_;
    }
    virtual std::string& get_wasm_name() {
        return wasm_name_;
    }
    virtual int32_t get_wasm_id() {
        return id_;
    }
    virtual void set_wasm_id(int32_t id) {
        id_ = id;
    }
private:
    wasm_functype_t *wasmtime_host_api_func(const wasm_wasmtime_host_api_t *api);
    void wasmtime_report_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
    bool wasm_wasmtime_has(std::string& name);

    wasm_wasmtime_plugin_t* plugin;
    int32_t wasm_ctx_id_ = 0;
    std::string wasm_name_;
    int32_t id_;

  wasmtime_val_t   param_int32[1] = {{ .kind = WASMTIME_I32 }};
    wasmtime_val_t   param_int32_int32[2] = {{ .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }};
    wasmtime_val_t   param_int32_int32_int32[3] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
};
    wasmtime_val_t   param_int32_int32_int32_int32[4] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
    { .kind = WASMTIME_I32 },
};
    wasmtime_val_t   param_int32_int32_int32_int32_int32[5] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
};
};

// wasmtime_vm.cc

#include "wasmtime_vm.h"
#include <iostream>

#define DEFINE_WASM_API(NAME, ARG_CHECK) \
    static wasm_trap_t* wasmtime_##NAME( \
        void *env, \
        wasmtime_caller_t *caller, \
        const wasmtime_val_t *args, \
        size_t nargs, \
        wasmtime_val_t *results, \
        size_t nresults \
    ) { \
        ARG_CHECK \
        results[0].kind = WASMTIME_I32; \
        results[0].of.i32 = res; \
        return NULL; \
    }
#define DEFINE_WASM_NAME(NAME, ARG) \
    {#NAME, wasmtime_##NAME, ARG},

#define DEFINE_WASM_NAME_ARG_VOID \
    0, {}
#define DEFINE_WASM_API_ARG_CHECK_VOID(NAME) \
    int32_t res = NAME();

#define DEFINE_WASM_NAME_ARG_I32_1 \
    1, { \
    WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_1(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t res = NAME(p0);

#define DEFINE_WASM_NAME_ARG_I32_2 \
    2, { \
    WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_2(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t res = NAME(p0, p1);

#define DEFINE_WASM_NAME_ARG_I32_3 \
    3, { \
    WASM_I32, WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_3(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t res = NAME(p0, p1, p2);

#define DEFINE_WASM_NAME_ARG_I32_4 \
    4, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_4(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3);

#define DEFINE_WASM_NAME_ARG_I32_5 \
    5, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
}
#define DEFINE_WASM_API_ARG_CHECK_I32_5(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4);

#define DEFINE_WASM_NAME_ARG_I32_6 \
    6, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
    WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_6(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t p5 = args[5].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4, p5);

#define DEFINE_WASM_NAME_ARG_I32_7 \
    7, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
    WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_7(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t p5 = args[5].of.i32; \
    int32_t p6 = args[6].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4, p5, p6);

DEFINE_WASM_API(tendis_get_buffer_bytes,
                DEFINE_WASM_API_ARG_CHECK_I32_4(tendis_get_buffer_bytes))

DEFINE_WASM_API(tendis_set_buffer_bytes,
                DEFINE_WASM_API_ARG_CHECK_I32_4(tendis_set_buffer_bytes))


static wasm_wasmtime_host_api_t host_apis[] = {
    DEFINE_WASM_NAME(tendis_get_buffer_bytes, DEFINE_WASM_NAME_ARG_I32_4)
    DEFINE_WASM_NAME(tendis_set_buffer_bytes, DEFINE_WASM_NAME_ARG_I32_4)
    { "", NULL, 0, {} }
};

wasm_functype_t *
WasmtimeVM::wasmtime_host_api_func(const wasm_wasmtime_host_api_t *api)
{
    int                i;
    wasm_valtype_vec_t param_vec, result_vec;
    wasm_valtype_t    *param[MAX_WASM_API_ARG];
    wasm_valtype_t    *result[1];
    wasm_functype_t   *f;

    for (i = 0; i < api->param_num; i++) {
        param[i] = wasm_valtype_new(api->param_type[i]);
    }

    result[0] = wasm_valtype_new(WASM_I32);
    wasm_valtype_vec_new(&param_vec, api->param_num, param);
    wasm_valtype_vec_new(&result_vec, 1, result);

    f = wasm_functype_new(&param_vec, &result_vec);
    return f;
}

void
WasmtimeVM::wasmtime_report_error(const char *message,
    wasmtime_error_t *error, wasm_trap_t *trap)
{
    wasm_byte_vec_t error_message;

    if (error != NULL) {
        wasmtime_error_message(error, &error_message);
        wasmtime_error_delete(error);
    } else {
        wasm_trap_message(trap, &error_message);
        wasm_trap_delete(trap);
    }
    std::cout << message << std::endl;
    std::cout.write(error_message.data, error_message.size);
    std::cout << std::endl;
    wasm_byte_vec_delete(&error_message);
}

int WasmtimeVM::load(std::string& bytecode, std::string& wasm_name) {
    size_t                        i;
    bool                          ok;
    wasm_engine_t                *vm_engine;
    wasm_trap_t                  *trap = NULL;
    wasmtime_module_t            *module;
    wasmtime_store_t             *store;
    wasmtime_context_t           *context;
    wasmtime_linker_t            *linker;
    wasi_config_t                *wasi_config;
    wasmtime_error_t             *error;
    wasmtime_extern_t             item;

    plugin = new wasm_wasmtime_plugin_t;
    wasm_name_ = wasm_name;

    vm_engine = wasm_engine_new();
    if (vm_engine == NULL) {
        std::cout << "failed to new engine" << std::endl;
        return -1;
    }
    //wasmtime_module_deserialize();
    error = wasmtime_module_new(vm_engine, (const uint8_t*) bytecode.c_str(), bytecode.size(), &module);
    //error = wasmtime_module_deserialize(vm_engine, (const uint8_t*) bytecode.c_str(), bytecode.size(), &module);
    if (error != NULL) {
        wasmtime_report_error("failed to new module: ", error, NULL);
        return -2;
    }

    store = wasmtime_store_new(vm_engine, NULL, NULL);
    if (store == NULL) {
        return -3;
    }

    context = wasmtime_store_context(store);

    wasi_config = wasi_config_new();
    if (wasi_config == NULL) {
        return -4;
    }

    wasi_config_inherit_env(wasi_config);
    wasi_config_inherit_stdin(wasi_config);
    wasi_config_inherit_stdout(wasi_config);
    wasi_config_inherit_stderr(wasi_config);

    error = wasmtime_context_set_wasi(context, wasi_config);
    if (error != NULL) {
        wasmtime_report_error("failed to init WASI: ", err
[message truncated]

view this post on Zulip Wasmtime GitHub notifications bot (Mar 05 2025 at 15:28):

alexcrichton commented on issue #10333:

When it comes to thread safety and the C/C++ API (and/or C/C++ in general) you're sort of in the wild west where you'll have to start from basics and build everything up yourself. There is basic documentation of thread safety in the C API and you can also browse the Rust API documentation to better understand thread safety.

tl;dr; "engine" is threadsafe, "store", and everything that refers to it, must only be used in one thread at a time. Your example at the top is only safe so long as nothing else on the main thread uses the store while it's in use by a child thread.

view this post on Zulip Wasmtime GitHub notifications bot (Mar 06 2025 at 01:35):

liupeidong0620 closed issue #10333:

question

  1. I created multiple wasm_wasmtime_plugin_t
  2. Each thread runs a wasm_wasmtime_plugin_t
  3. Is this thread safe?
  4. The wasmtime_func_call function calls the malloc function exported by wasm. Is this thread safe?
typedef struct { // Load the same wasm program and create multiple wasm_wasmtime_plugin_t instances
    wasm_engine_t           *vm_engine;
    wasmtime_module_t       *module;
    wasmtime_store_t        *store;
    wasmtime_context_t      *context;
    wasmtime_linker_t       *linker;
    wasmtime_instance_t      instance;
    wasmtime_memory_t        memory;
} wasm_wasmtime_plugin_t;

int main() {
....
wasm_wasmtime_plugin_t plugins[6];
// Load the same wasm program and create multiple wasm_wasmtime_plugin_t instances

for (int i = 0; i < 6; i ++) {
std::thread t([plugin_num = i] {
       //  Is it thread safe?
        error = wasmtime_func_call(plugins[plugin_num]->context, &func.of.func, params, param_num, results,
                               has_result ? 1 : 0, &trap);
    });
}
// wait thread ok
....


}

code (wasm_wasmtime_plugin_t initialization process, specific code)

// wasmtime_vm.h

#pragma once

#include <wasi.h>
#include <wasm.h>
#include <wasmtime.h>

#include "wasm_base.h"
#include "wasm_api.h"

#include <string>

typedef struct {
    wasm_engine_t           *vm_engine;
    wasmtime_module_t       *module;
    wasmtime_store_t        *store;
    wasmtime_context_t      *context;
    wasmtime_linker_t       *linker;
    wasmtime_instance_t      instance;
    wasmtime_memory_t        memory;
} wasm_wasmtime_plugin_t;

typedef struct {
    std::string                name;
    wasmtime_func_callback_t cb;
    int8_t                   param_num;
    wasm_valkind_t           param_type[MAX_WASM_API_ARG];
} wasm_wasmtime_host_api_t;

class WasmtimeVM : public WasmBaseVM {
public:
    WasmtimeVM() {}
    virtual int load(std::string& wasm_file, std::string& wasm_name); // load wasm and init
    virtual int32_t wasm_call(std::string func_name, bool has_result, int param_type, ...); // call wasm func

    virtual int32_t wasm_memory_alloc(int32_t size);
    virtual char* wasm_get_memory(int32_t addr, int32_t size);

    virtual ~WasmtimeVM();


    virtual void setCtxId(int32_t wasm_ctx_id) {
        wasm_ctx_id_ = wasm_ctx_id;
    }
    virtual int32_t getCtxId() {
        return wasm_ctx_id_;
    }
    virtual std::string& get_wasm_name() {
        return wasm_name_;
    }
    virtual int32_t get_wasm_id() {
        return id_;
    }
    virtual void set_wasm_id(int32_t id) {
        id_ = id;
    }
private:
    wasm_functype_t *wasmtime_host_api_func(const wasm_wasmtime_host_api_t *api);
    void wasmtime_report_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
    bool wasm_wasmtime_has(std::string& name);

    wasm_wasmtime_plugin_t* plugin;
    int32_t wasm_ctx_id_ = 0;
    std::string wasm_name_;
    int32_t id_;

  wasmtime_val_t   param_int32[1] = {{ .kind = WASMTIME_I32 }};
    wasmtime_val_t   param_int32_int32[2] = {{ .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }};
    wasmtime_val_t   param_int32_int32_int32[3] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
};
    wasmtime_val_t   param_int32_int32_int32_int32[4] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
    { .kind = WASMTIME_I32 },
};
    wasmtime_val_t   param_int32_int32_int32_int32_int32[5] = {
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
    { .kind = WASMTIME_I32 }, { .kind = WASMTIME_I32 },
};
};

// wasmtime_vm.cc

#include "wasmtime_vm.h"
#include <iostream>

#define DEFINE_WASM_API(NAME, ARG_CHECK) \
    static wasm_trap_t* wasmtime_##NAME( \
        void *env, \
        wasmtime_caller_t *caller, \
        const wasmtime_val_t *args, \
        size_t nargs, \
        wasmtime_val_t *results, \
        size_t nresults \
    ) { \
        ARG_CHECK \
        results[0].kind = WASMTIME_I32; \
        results[0].of.i32 = res; \
        return NULL; \
    }
#define DEFINE_WASM_NAME(NAME, ARG) \
    {#NAME, wasmtime_##NAME, ARG},

#define DEFINE_WASM_NAME_ARG_VOID \
    0, {}
#define DEFINE_WASM_API_ARG_CHECK_VOID(NAME) \
    int32_t res = NAME();

#define DEFINE_WASM_NAME_ARG_I32_1 \
    1, { \
    WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_1(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t res = NAME(p0);

#define DEFINE_WASM_NAME_ARG_I32_2 \
    2, { \
    WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_2(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t res = NAME(p0, p1);

#define DEFINE_WASM_NAME_ARG_I32_3 \
    3, { \
    WASM_I32, WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_3(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t res = NAME(p0, p1, p2);

#define DEFINE_WASM_NAME_ARG_I32_4 \
    4, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_4(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3);

#define DEFINE_WASM_NAME_ARG_I32_5 \
    5, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
}
#define DEFINE_WASM_API_ARG_CHECK_I32_5(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4);

#define DEFINE_WASM_NAME_ARG_I32_6 \
    6, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
    WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_6(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t p5 = args[5].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4, p5);

#define DEFINE_WASM_NAME_ARG_I32_7 \
    7, { \
    WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32,  \
    WASM_I32, WASM_I32, }
#define DEFINE_WASM_API_ARG_CHECK_I32_7(NAME) \
    int32_t p0 = args[0].of.i32; \
    int32_t p1 = args[1].of.i32; \
    int32_t p2 = args[2].of.i32; \
    int32_t p3 = args[3].of.i32; \
    int32_t p4 = args[4].of.i32; \
    int32_t p5 = args[5].of.i32; \
    int32_t p6 = args[6].of.i32; \
    int32_t res = NAME(p0, p1, p2, p3, p4, p5, p6);

DEFINE_WASM_API(tendis_get_buffer_bytes,
                DEFINE_WASM_API_ARG_CHECK_I32_4(tendis_get_buffer_bytes))

DEFINE_WASM_API(tendis_set_buffer_bytes,
                DEFINE_WASM_API_ARG_CHECK_I32_4(tendis_set_buffer_bytes))


static wasm_wasmtime_host_api_t host_apis[] = {
    DEFINE_WASM_NAME(tendis_get_buffer_bytes, DEFINE_WASM_NAME_ARG_I32_4)
    DEFINE_WASM_NAME(tendis_set_buffer_bytes, DEFINE_WASM_NAME_ARG_I32_4)
    { "", NULL, 0, {} }
};

wasm_functype_t *
WasmtimeVM::wasmtime_host_api_func(const wasm_wasmtime_host_api_t *api)
{
    int                i;
    wasm_valtype_vec_t param_vec, result_vec;
    wasm_valtype_t    *param[MAX_WASM_API_ARG];
    wasm_valtype_t    *result[1];
    wasm_functype_t   *f;

    for (i = 0; i < api->param_num; i++) {
        param[i] = wasm_valtype_new(api->param_type[i]);
    }

    result[0] = wasm_valtype_new(WASM_I32);
    wasm_valtype_vec_new(&param_vec, api->param_num, param);
    wasm_valtype_vec_new(&result_vec, 1, result);

    f = wasm_functype_new(&param_vec, &result_vec);
    return f;
}

void
WasmtimeVM::wasmtime_report_error(const char *message,
    wasmtime_error_t *error, wasm_trap_t *trap)
{
    wasm_byte_vec_t error_message;

    if (error != NULL) {
        wasmtime_error_message(error, &error_message);
        wasmtime_error_delete(error);
    } else {
        wasm_trap_message(trap, &error_message);
        wasm_trap_delete(trap);
    }
    std::cout << message << std::endl;
    std::cout.write(error_message.data, error_message.size);
    std::cout << std::endl;
    wasm_byte_vec_delete(&error_message);
}

int WasmtimeVM::load(std::string& bytecode, std::string& wasm_name) {
    size_t                        i;
    bool                          ok;
    wasm_engine_t                *vm_engine;
    wasm_trap_t                  *trap = NULL;
    wasmtime_module_t            *module;
    wasmtime_store_t             *store;
    wasmtime_context_t           *context;
    wasmtime_linker_t            *linker;
    wasi_config_t                *wasi_config;
    wasmtime_error_t             *error;
    wasmtime_extern_t             item;

    plugin = new wasm_wasmtime_plugin_t;
    wasm_name_ = wasm_name;

    vm_engine = wasm_engine_new();
    if (vm_engine == NULL) {
        std::cout << "failed to new engine" << std::endl;
        return -1;
    }
    //wasmtime_module_deserialize();
    error = wasmtime_module_new(vm_engine, (const uint8_t*) bytecode.c_str(), bytecode.size(), &module);
    //error = wasmtime_module_deserialize(vm_engine, (const uint8_t*) bytecode.c_str(), bytecode.size(), &module);
    if (error != NULL) {
        wasmtime_report_error("failed to new module: ", error, NULL);
        return -2;
    }

    store = wasmtime_store_new(vm_engine, NULL, NULL);
    if (store == NULL) {
        return -3;
    }

    context = wasmtime_store_context(store);

    wasi_config = wasi_config_new();
    if (wasi_config == NULL) {
        return -4;
    }

    wasi_config_inherit_env(wasi_config);
    wasi_config_inherit_stdin(wasi_config);
    wasi_config_inherit_stdout(wasi_config);
    wasi_config_inherit_stderr(wasi_config);

    error = wasmtime_context_set_wasi(context, wasi_config);
    if (error != NULL) {
        wasmtime_report_error("failed to init WASI: ", err
[message truncated]

view this post on Zulip Wasmtime GitHub notifications bot (Mar 06 2025 at 01:35):

liupeidong0620 commented on issue #10333:

Thanks


Last updated: Apr 17 2025 at 08:04 UTC