Implement module instance context APIs (#2436)

Introduce module instance context APIs which can set one or more contexts created
by the embedder for a wasm module instance:
```C
    wasm_runtime_create_context_key
    wasm_runtime_destroy_context_key
    wasm_runtime_set_context
    wasm_runtime_set_context_spread
    wasm_runtime_get_context
```

And make libc-wasi use it and set wasi context as the first context bound to the wasm
module instance.

Also add samples.

Refer to https://github.com/bytecodealliance/wasm-micro-runtime/issues/2460.
This commit is contained in:
YAMAMOTO Takashi
2023-09-07 15:54:11 +09:00
committed by GitHub
parent af2f3c8759
commit 6c846acc59
35 changed files with 1190 additions and 56 deletions

View File

@ -480,4 +480,9 @@
#define WASM_MEM_DUAL_BUS_MIRROR 0
#endif
/* The max number of module instance contexts. */
#ifndef WASM_MAX_INSTANCE_CONTEXTS
#define WASM_MAX_INSTANCE_CONTEXTS 8
#endif
#endif /* end of _CONFIG_H_ */

View File

@ -1276,12 +1276,10 @@ aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst)
->common.c_api_func_imports);
if (!is_sub_inst) {
#if WASM_ENABLE_LIBC_WASI != 0
wasm_runtime_destroy_wasi((WASMModuleInstanceCommon *)module_inst);
#endif
#if WASM_ENABLE_WASI_NN != 0
wasi_nn_destroy(module_inst);
#endif
wasm_native_call_context_dtors((WASMModuleInstanceCommon *)module_inst);
}
wasm_runtime_free(module_inst);

View File

@ -6,6 +6,15 @@
#include "wasm_native.h"
#include "wasm_runtime_common.h"
#include "bh_log.h"
#if WASM_ENABLE_INTERP != 0
#include "../interpreter/wasm_runtime.h"
#endif
#if WASM_ENABLE_AOT != 0
#include "../aot/aot_runtime.h"
#endif
#if WASM_ENABLE_THREAD_MGR != 0
#include "../libraries/thread-mgr/thread_manager.h"
#endif
#if !defined(BH_PLATFORM_ZEPHYR) && !defined(BH_PLATFORM_ALIOS_THINGS) \
&& !defined(BH_PLATFORM_OPENRTOS) && !defined(BH_PLATFORM_ESP_IDF)
@ -22,6 +31,10 @@
static NativeSymbolsList g_native_symbols_list = NULL;
#if WASM_ENABLE_LIBC_WASI != 0
static void *g_wasi_context_key;
#endif /* WASM_ENABLE_LIBC_WASI */
uint32
get_libc_builtin_export_apis(NativeSymbol **p_libc_builtin_apis);
@ -394,6 +407,155 @@ wasm_native_unregister_natives(const char *module_name,
return false;
}
#if WASM_ENABLE_MODULE_INST_CONTEXT != 0
static uint32
context_key_to_idx(void *key)
{
bh_assert(key != NULL);
uint32 idx = (uint32)(uintptr_t)key;
bh_assert(idx > 0);
bh_assert(idx <= WASM_MAX_INSTANCE_CONTEXTS);
return idx - 1;
}
static void *
context_idx_to_key(uint32 idx)
{
bh_assert(idx < WASM_MAX_INSTANCE_CONTEXTS);
return (void *)(uintptr_t)(idx + 1);
}
typedef void (*dtor_t)(WASMModuleInstanceCommon *, void *);
static dtor_t g_context_dtors[WASM_MAX_INSTANCE_CONTEXTS];
static void
dtor_noop(WASMModuleInstanceCommon *inst, void *ctx)
{}
void *
wasm_native_create_context_key(void (*dtor)(WASMModuleInstanceCommon *inst,
void *ctx))
{
uint32 i;
for (i = 0; i < WASM_MAX_INSTANCE_CONTEXTS; i++) {
if (g_context_dtors[i] == NULL) {
if (dtor == NULL) {
dtor = dtor_noop;
}
g_context_dtors[i] = dtor;
return context_idx_to_key(i);
}
}
LOG_ERROR("failed to allocate instance context key");
return NULL;
}
void
wasm_native_destroy_context_key(void *key)
{
uint32 idx = context_key_to_idx(key);
bh_assert(g_context_dtors[idx] != NULL);
g_context_dtors[idx] = NULL;
}
static WASMModuleInstanceExtraCommon *
wasm_module_inst_extra_common(WASMModuleInstanceCommon *inst)
{
#if WASM_ENABLE_INTERP != 0
if (inst->module_type == Wasm_Module_Bytecode) {
return &((WASMModuleInstance *)inst)->e->common;
}
#endif
#if WASM_ENABLE_AOT != 0
if (inst->module_type == Wasm_Module_AoT) {
return &((AOTModuleInstanceExtra *)((AOTModuleInstance *)inst)->e)
->common;
}
#endif
bh_assert(false);
return NULL;
}
void
wasm_native_set_context(WASMModuleInstanceCommon *inst, void *key, void *ctx)
{
uint32 idx = context_key_to_idx(key);
WASMModuleInstanceExtraCommon *common = wasm_module_inst_extra_common(inst);
common->contexts[idx] = ctx;
}
void
wasm_native_set_context_spread(WASMModuleInstanceCommon *inst, void *key,
void *ctx)
{
#if WASM_ENABLE_THREAD_MGR != 0
wasm_cluster_set_context(inst, key, ctx);
#else
wasm_native_set_context(inst, key, ctx);
#endif
}
void *
wasm_native_get_context(WASMModuleInstanceCommon *inst, void *key)
{
uint32 idx = context_key_to_idx(key);
WASMModuleInstanceExtraCommon *common = wasm_module_inst_extra_common(inst);
return common->contexts[idx];
}
void
wasm_native_call_context_dtors(WASMModuleInstanceCommon *inst)
{
WASMModuleInstanceExtraCommon *common = wasm_module_inst_extra_common(inst);
uint32 i;
for (i = 0; i < WASM_MAX_INSTANCE_CONTEXTS; i++) {
dtor_t dtor = g_context_dtors[i];
if (dtor != NULL) {
dtor(inst, common->contexts[i]);
}
}
}
void
wasm_native_inherit_contexts(WASMModuleInstanceCommon *child,
WASMModuleInstanceCommon *parent)
{
WASMModuleInstanceExtraCommon *parent_common =
wasm_module_inst_extra_common(parent);
WASMModuleInstanceExtraCommon *child_common =
wasm_module_inst_extra_common(child);
bh_memcpy_s(child_common->contexts,
sizeof(*child_common->contexts) * WASM_MAX_INSTANCE_CONTEXTS,
parent_common->contexts,
sizeof(*parent_common->contexts) * WASM_MAX_INSTANCE_CONTEXTS);
}
#endif /* WASM_ENABLE_MODULE_INST_CONTEXT != 0 */
#if WASM_ENABLE_LIBC_WASI != 0
WASIContext *
wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst_comm)
{
return wasm_native_get_context(module_inst_comm, g_wasi_context_key);
}
void
wasm_runtime_set_wasi_ctx(WASMModuleInstanceCommon *module_inst_comm,
WASIContext *wasi_ctx)
{
return wasm_native_set_context(module_inst_comm, g_wasi_context_key,
wasi_ctx);
}
static void
wasi_context_dtor(WASMModuleInstanceCommon *inst, void *ctx)
{
if (ctx == NULL) {
return;
}
wasm_runtime_destroy_wasi(inst);
}
#endif /* end of WASM_ENABLE_LIBC_WASI */
bool
wasm_native_init()
{
@ -420,6 +582,10 @@ wasm_native_init()
#endif /* WASM_ENABLE_SPEC_TEST */
#if WASM_ENABLE_LIBC_WASI != 0
g_wasi_context_key = wasm_native_create_context_key(wasi_context_dtor);
if (g_wasi_context_key == NULL) {
goto fail;
}
n_native_symbols = get_libc_wasi_export_apis(&native_symbols);
if (!wasm_native_register_natives("wasi_unstable", native_symbols,
n_native_symbols))
@ -507,6 +673,12 @@ wasm_native_destroy()
{
NativeSymbolsNode *node, *node_next;
#if WASM_ENABLE_LIBC_WASI != 0
if (g_wasi_context_key != NULL) {
wasm_native_destroy_context_key(g_wasi_context_key);
g_wasi_context_key = NULL;
}
#endif
#if WASM_ENABLE_LIB_PTHREAD != 0
lib_pthread_destroy();
#endif

View File

@ -68,6 +68,36 @@ bool
wasm_native_unregister_natives(const char *module_name,
NativeSymbol *native_symbols);
#if WASM_ENABLE_MODULE_INST_CONTEXT != 0
struct WASMModuleInstanceCommon;
void *
wasm_native_create_context_key(
void (*dtor)(struct WASMModuleInstanceCommon *inst, void *ctx));
void
wasm_native_destroy_context_key(void *key);
void
wasm_native_set_context(struct WASMModuleInstanceCommon *inst, void *key,
void *ctx);
void
wasm_native_set_context_spread(struct WASMModuleInstanceCommon *inst, void *key,
void *ctx);
void *
wasm_native_get_context(struct WASMModuleInstanceCommon *inst, void *key);
void
wasm_native_call_context_dtors(struct WASMModuleInstanceCommon *inst);
void
wasm_native_inherit_contexts(struct WASMModuleInstanceCommon *child,
struct WASMModuleInstanceCommon *parent);
#else /* WASM_ENABLE_MODULE_INST_CONTEXT */
#define wasm_native_call_context_dtors(inst) (void)(inst)
#define wasm_native_inherit_contexts(child, parent) (void)(parent)
#endif /* WASM_ENABLE_MODULE_INST_CONTEXT */
bool
wasm_native_init();

View File

@ -3311,27 +3311,6 @@ wasm_runtime_get_wasi_exit_code(WASMModuleInstanceCommon *module_inst)
#endif
return wasi_ctx->exit_code;
}
WASIContext *
wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst_comm)
{
WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm;
bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode
|| module_inst_comm->module_type == Wasm_Module_AoT);
return module_inst->wasi_ctx;
}
void
wasm_runtime_set_wasi_ctx(WASMModuleInstanceCommon *module_inst_comm,
WASIContext *wasi_ctx)
{
WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm;
bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode
|| module_inst_comm->module_type == Wasm_Module_AoT);
module_inst->wasi_ctx = wasi_ctx;
}
#endif /* end of WASM_ENABLE_LIBC_WASI */
WASMModuleCommon *
@ -5681,3 +5660,37 @@ wasm_runtime_is_import_global_linked(const char *module_name,
return false;
#endif
}
#if WASM_ENABLE_MODULE_INST_CONTEXT != 0
void *
wasm_runtime_create_context_key(void (*dtor)(WASMModuleInstanceCommon *inst,
void *ctx))
{
return wasm_native_create_context_key(dtor);
}
void
wasm_runtime_destroy_context_key(void *key)
{
wasm_native_destroy_context_key(key);
}
void
wasm_runtime_set_context(WASMModuleInstanceCommon *inst, void *key, void *ctx)
{
wasm_native_set_context(inst, key, ctx);
}
void
wasm_runtime_set_context_spread(WASMModuleInstanceCommon *inst, void *key,
void *ctx)
{
wasm_native_set_context_spread(inst, key, ctx);
}
void *
wasm_runtime_get_context(WASMModuleInstanceCommon *inst, void *key)
{
return wasm_native_get_context(inst, key);
}
#endif /* WASM_ENABLE_MODULE_INST_CONTEXT != 0 */

View File

@ -939,6 +939,26 @@ WASM_RUNTIME_API_EXTERN bool
wasm_runtime_unregister_natives(const char *module_name,
NativeSymbol *native_symbols);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN void *
wasm_runtime_create_context_key(void (*dtor)(WASMModuleInstanceCommon *inst,
void *ctx));
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN void
wasm_runtime_destroy_context_key(void *key);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN void
wasm_runtime_set_context(WASMModuleInstanceCommon *inst, void *key, void *ctx);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN void
wasm_runtime_set_context_spread(WASMModuleInstanceCommon *inst, void *key,
void *ctx);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN void *
wasm_runtime_get_context(WASMModuleInstanceCommon *inst, void *key);
bool
wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr,
const WASMType *func_type, const char *signature,

View File

@ -1456,6 +1456,74 @@ WASM_RUNTIME_API_EXTERN void
wasm_runtime_set_enlarge_mem_error_callback(
const enlarge_memory_error_callback_t callback);
/*
* module instance context APIs
* wasm_runtime_create_context_key
* wasm_runtime_destroy_context_key
* wasm_runtime_set_context
* wasm_runtime_set_context_spread
* wasm_runtime_get_context
*
* This set of APIs is intended to be used by an embedder which provides
* extra sets of native functions, which need per module instance state
* and are maintained outside of the WAMR tree.
*
* It's modelled after the pthread specific API.
*
* wasm_runtime_set_context_spread is similar to
* wasm_runtime_set_context, except that
* wasm_runtime_set_context_spread applies the change
* to all threads in the cluster.
* It's an undefined behavior if multiple threads in a cluster call
* wasm_runtime_set_context_spread on the same key
* simultaneously. It's a caller's resposibility to perform necessary
* serialization if necessary. For example:
*
* if (wasm_runtime_get_context(inst, key) == NULL) {
* newctx = alloc_and_init(...);
* lock(some_lock);
* if (wasm_runtime_get_context(inst, key) == NULL) {
* // this thread won the race
* wasm_runtime_set_context_spread(inst, key, newctx);
* newctx = NULL;
* }
* unlock(some_lock);
* if (newctx != NULL) {
* // this thread lost the race, free it
* cleanup_and_free(newctx);
* }
* }
*
* Note: dynamic key create/destroy while instances are live is not
* implemented as of writing this.
* it's caller's resposibility to ensure destorying all module instances
* before calling wasm_runtime_create_context_key or
* wasm_runtime_destroy_context_key.
* otherwise, it's an undefined behavior.
*
* Note about threads:
* - When spawning a thread, the contexts (the pointers given to
* wasm_runtime_set_context) are copied from the parent
* instance.
* - The destructor is called only on the main instance.
*/
WASM_RUNTIME_API_EXTERN void *
wasm_runtime_create_context_key(
void (*dtor)(wasm_module_inst_t inst, void *ctx));
WASM_RUNTIME_API_EXTERN void
wasm_runtime_destroy_context_key(void *key);
WASM_RUNTIME_API_EXTERN void
wasm_runtime_set_context(wasm_module_inst_t inst, void *key,
void *ctx);
WASM_RUNTIME_API_EXTERN void
wasm_runtime_set_context_spread(wasm_module_inst_t inst, void *key,
void *ctx);
WASM_RUNTIME_API_EXTERN void *
wasm_runtime_get_context(wasm_module_inst_t inst, void *key);
/* clang-format on */
#ifdef __cplusplus

View File

@ -2233,12 +2233,10 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst)
wasm_runtime_free(module_inst->e->common.c_api_func_imports);
if (!is_sub_inst) {
#if WASM_ENABLE_LIBC_WASI != 0
wasm_runtime_destroy_wasi((WASMModuleInstanceCommon *)module_inst);
#endif
#if WASM_ENABLE_WASI_NN != 0
wasi_nn_destroy(module_inst);
#endif
wasm_native_call_context_dtors((WASMModuleInstanceCommon *)module_inst);
}
wasm_runtime_free(module_inst);

View File

@ -212,6 +212,7 @@ typedef struct CApiFuncImport {
/* The common part of WASMModuleInstanceExtra and AOTModuleInstanceExtra */
typedef struct WASMModuleInstanceExtraCommon {
void *contexts[WASM_MAX_INSTANCE_CONTEXTS];
CApiFuncImport *c_api_func_imports;
/* pointer to the exec env currently used */
WASMExecEnv *cur_exec_env;
@ -299,12 +300,8 @@ struct WASMModuleInstance {
it denotes `AOTModule *` */
DefPointer(WASMModule *, module);
#if WASM_ENABLE_LIBC_WASI
/* WASI context */
DefPointer(WASIContext *, wasi_ctx);
#else
DefPointer(void *, wasi_ctx);
#endif
DefPointer(void *, used_to_be_wasi_ctx); /* unused */
DefPointer(WASMExecEnv *, exec_env_singleton);
/* Array of function pointers to import functions,
not available in AOTModuleInstance */

View File

@ -559,9 +559,6 @@ pthread_create_wrapper(wasm_exec_env_t exec_env,
uint32 thread_handle;
uint32 stack_size = 8192;
int32 ret = -1;
#if WASM_ENABLE_LIBC_WASI != 0
WASIContext *wasi_ctx;
#endif
bh_assert(module);
bh_assert(module_inst);
@ -588,11 +585,7 @@ pthread_create_wrapper(wasm_exec_env_t exec_env,
wasm_runtime_set_custom_data_internal(
new_module_inst, wasm_runtime_get_custom_data(module_inst));
#if WASM_ENABLE_LIBC_WASI != 0
wasi_ctx = get_wasi_ctx(module_inst);
if (wasi_ctx)
wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx);
#endif
wasm_native_inherit_contexts(new_module_inst, module_inst);
if (!(wasm_cluster_dup_c_api_imports(new_module_inst, module_inst)))
goto fail;

View File

@ -80,9 +80,6 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg)
int32 thread_id;
uint32 stack_size = 8192;
int32 ret = -1;
#if WASM_ENABLE_LIBC_WASI != 0
WASIContext *wasi_ctx;
#endif
bh_assert(module);
bh_assert(module_inst);
@ -99,11 +96,7 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg)
if (!(wasm_cluster_dup_c_api_imports(new_module_inst, module_inst)))
goto thread_preparation_fail;
#if WASM_ENABLE_LIBC_WASI != 0
wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst);
if (wasi_ctx)
wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx);
#endif
wasm_native_inherit_contexts(new_module_inst, module_inst);
start_func = wasm_runtime_lookup_function(new_module_inst,
THREAD_START_FUNCTION, NULL);

View File

@ -480,9 +480,6 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env)
wasm_module_inst_t module_inst = get_module_inst(exec_env);
wasm_module_t module;
wasm_module_inst_t new_module_inst;
#if WASM_ENABLE_LIBC_WASI != 0
WASIContext *wasi_ctx;
#endif
WASMExecEnv *new_exec_env;
uint32 aux_stack_start, aux_stack_size;
uint32 stack_size = 8192;
@ -520,10 +517,7 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env)
wasm_runtime_set_custom_data_internal(
new_module_inst, wasm_runtime_get_custom_data(module_inst));
#if WASM_ENABLE_LIBC_WASI != 0
wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst);
wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx);
#endif
wasm_native_inherit_contexts(new_module_inst, module_inst);
new_exec_env = wasm_exec_env_create_internal(new_module_inst,
exec_env->wasm_stack_size);
@ -1324,6 +1318,48 @@ wasm_cluster_spread_custom_data(WASMModuleInstanceCommon *module_inst,
}
}
#if WASM_ENABLE_MODULE_INST_CONTEXT != 0
struct inst_set_context_data {
void *key;
void *ctx;
};
static void
set_context_visitor(void *node, void *user_data)
{
WASMExecEnv *curr_exec_env = (WASMExecEnv *)node;
WASMModuleInstanceCommon *module_inst = get_module_inst(curr_exec_env);
const struct inst_set_context_data *data = user_data;
wasm_runtime_set_context(module_inst, data->key, data->ctx);
}
void
wasm_cluster_set_context(WASMModuleInstanceCommon *module_inst, void *key,
void *ctx)
{
WASMExecEnv *exec_env = wasm_clusters_search_exec_env(module_inst);
if (exec_env == NULL) {
/* Maybe threads have not been started yet. */
wasm_runtime_set_context(module_inst, key, ctx);
}
else {
WASMCluster *cluster;
struct inst_set_context_data data;
data.key = key;
data.ctx = ctx;
cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster);
os_mutex_lock(&cluster->lock);
traverse_list(&cluster->exec_env_list, set_context_visitor, &data);
os_mutex_unlock(&cluster->lock);
}
}
#endif /* WASM_ENABLE_MODULE_INST_CONTEXT != 0 */
bool
wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env)
{

View File

@ -151,6 +151,10 @@ void
wasm_cluster_spread_custom_data(WASMModuleInstanceCommon *module_inst,
void *custom_data);
void
wasm_cluster_set_context(WASMModuleInstanceCommon *module_inst, void *key,
void *ctx);
bool
wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env);