Эх сурвалжийг харах

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.
YAMAMOTO Takashi 2 жил өмнө
parent
commit
6c846acc59
35 өөрчлөгдсөн 1190 нэмэгдсэн , 56 устгасан
  1. 4 0
      build-scripts/config_common.cmake
  2. 2 0
      build-scripts/runtime_lib.cmake
  3. 5 0
      core/config.h
  4. 1 3
      core/iwasm/aot/aot_runtime.c
  5. 172 0
      core/iwasm/common/wasm_native.c
  6. 30 0
      core/iwasm/common/wasm_native.h
  7. 34 21
      core/iwasm/common/wasm_runtime_common.c
  8. 20 0
      core/iwasm/common/wasm_runtime_common.h
  9. 68 0
      core/iwasm/include/wasm_export.h
  10. 1 3
      core/iwasm/interpreter/wasm_runtime.c
  11. 3 6
      core/iwasm/interpreter/wasm_runtime.h
  12. 1 8
      core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c
  13. 1 8
      core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c
  14. 43 7
      core/iwasm/libraries/thread-mgr/thread_manager.c
  15. 4 0
      core/iwasm/libraries/thread-mgr/thread_manager.h
  16. 8 0
      product-mini/platforms/nuttx/wamr.mk
  17. 1 0
      samples/inst-context-threads/.gitignore
  18. 92 0
      samples/inst-context-threads/CMakeLists.txt
  19. 4 0
      samples/inst-context-threads/README.md
  20. 61 0
      samples/inst-context-threads/build.sh
  21. 3 0
      samples/inst-context-threads/run.sh
  22. 151 0
      samples/inst-context-threads/src/main.c
  23. 11 0
      samples/inst-context-threads/src/my_context.h
  24. 32 0
      samples/inst-context-threads/src/native_impl.c
  25. 65 0
      samples/inst-context-threads/wasm-apps/testapp.c
  26. 1 0
      samples/inst-context/.gitignore
  27. 91 0
      samples/inst-context/CMakeLists.txt
  28. 4 0
      samples/inst-context/README.md
  29. 63 0
      samples/inst-context/build.sh
  30. 3 0
      samples/inst-context/run.sh
  31. 166 0
      samples/inst-context/src/main.c
  32. 10 0
      samples/inst-context/src/my_context.h
  33. 15 0
      samples/inst-context/src/native_impl.c
  34. 19 0
      samples/inst-context/wasm-apps/testapp.c
  35. 1 0
      wamr-compiler/CMakeLists.txt

+ 4 - 0
build-scripts/config_common.cmake

@@ -378,6 +378,10 @@ if (WAMR_BUILD_WASM_CACHE EQUAL 1)
   add_definitions (-DWASM_ENABLE_WASM_CACHE=1)
   message ("     Wasm files cache enabled")
 endif ()
+if (WAMR_BUILD_MODULE_INST_CONTEXT EQUAL 1)
+  add_definitions (-DWASM_ENABLE_MODULE_INST_CONTEXT=1)
+  message ("     Module instance context enabled")
+endif ()
 if (WAMR_BUILD_GC_HEAP_VERIFY EQUAL 1)
   add_definitions (-DWASM_ENABLE_GC_VERIFY=1)
   message ("     GC heap verification enabled")

+ 2 - 0
build-scripts/runtime_lib.cmake

@@ -91,8 +91,10 @@ endif ()
 
 if (WAMR_BUILD_LIBC_UVWASI EQUAL 1)
     include (${IWASM_DIR}/libraries/libc-uvwasi/libc_uvwasi.cmake)
+    set (WAMR_BUILD_MODULE_INST_CONTEXT 1)
 elseif (WAMR_BUILD_LIBC_WASI EQUAL 1)
     include (${IWASM_DIR}/libraries/libc-wasi/libc_wasi.cmake)
+    set (WAMR_BUILD_MODULE_INST_CONTEXT 1)
 endif ()
 
 if (WAMR_BUILD_LIB_PTHREAD_SEMAPHORE EQUAL 1)

+ 5 - 0
core/config.h

@@ -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_ */

+ 1 - 3
core/iwasm/aot/aot_runtime.c

@@ -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);

+ 172 - 0
core/iwasm/common/wasm_native.c

@@ -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

+ 30 - 0
core/iwasm/common/wasm_native.h

@@ -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();
 

+ 34 - 21
core/iwasm/common/wasm_runtime_common.c

@@ -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 */

+ 20 - 0
core/iwasm/common/wasm_runtime_common.h

@@ -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,

+ 68 - 0
core/iwasm/include/wasm_export.h

@@ -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

+ 1 - 3
core/iwasm/interpreter/wasm_runtime.c

@@ -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);

+ 3 - 6
core/iwasm/interpreter/wasm_runtime.h

@@ -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 */

+ 1 - 8
core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c

@@ -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;

+ 1 - 8
core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c

@@ -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);

+ 43 - 7
core/iwasm/libraries/thread-mgr/thread_manager.c

@@ -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)
 {

+ 4 - 0
core/iwasm/libraries/thread-mgr/thread_manager.h

@@ -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);
 

+ 8 - 0
product-mini/platforms/nuttx/wamr.mk

@@ -253,10 +253,18 @@ CSRCS += posix.c
 CSRCS += random.c
 CSRCS += str.c
 VPATH += $(IWASM_ROOT)/libraries/libc-wasi/sandboxed-system-primitives/src
+# todo: use Kconfig select instead
+CONFIG_INTERPRETERS_WAMR_MODULE_INSTANCE_CONTEXT = y
 else
 CFLAGS += -DWASM_ENABLE_LIBC_WASI=0
 endif
 
+ifeq ($(CONFIG_INTERPRETERS_WAMR_MODULE_INSTANCE_CONTEXT),y)
+CFLAGS += -DWASM_ENABLE_MODULE_INST_CONTEXT=1
+else
+CFLAGS += -DWASM_ENABLE_MODULE_INST_CONTEXT=0
+endif
+
 ifeq ($(CONFIG_INTERPRETERS_WAMR_MULTI_MODULE),y)
 CFLAGS += -DWASM_ENABLE_MULTI_MODULE=1
 else

+ 1 - 0
samples/inst-context-threads/.gitignore

@@ -0,0 +1 @@
+/out/

+ 92 - 0
samples/inst-context-threads/CMakeLists.txt

@@ -0,0 +1,92 @@
+# Copyright (C) 2019 Intel Corporation.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+cmake_minimum_required (VERSION 3.14)
+
+include(CheckPIESupported)
+
+if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows")
+  project (inst-context)
+else()
+  project (inst-context C ASM)
+  enable_language (ASM_MASM)
+endif()
+
+################  runtime settings  ################
+string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM)
+if (APPLE)
+  add_definitions(-DBH_PLATFORM_DARWIN)
+endif ()
+
+# Reset default linker flags
+set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
+
+# WAMR features switch
+
+# Set WAMR_BUILD_TARGET, currently values supported:
+# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]",
+# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]"
+if (NOT DEFINED WAMR_BUILD_TARGET)
+  if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)")
+    set (WAMR_BUILD_TARGET "AARCH64")
+  elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
+    set (WAMR_BUILD_TARGET "RISCV64")
+  elseif (CMAKE_SIZEOF_VOID_P EQUAL 8)
+    # Build as X86_64 by default in 64-bit platform
+    set (WAMR_BUILD_TARGET "X86_64")
+  elseif (CMAKE_SIZEOF_VOID_P EQUAL 4)
+    # Build as X86_32 by default in 32-bit platform
+    set (WAMR_BUILD_TARGET "X86_32")
+  else ()
+    message(SEND_ERROR "Unsupported build target platform!")
+  endif ()
+endif ()
+
+if (NOT CMAKE_BUILD_TYPE)
+  set (CMAKE_BUILD_TYPE Debug)
+endif ()
+
+set (WAMR_BUILD_INTERP 1)
+set (WAMR_BUILD_AOT 1)
+set (WAMR_BUILD_JIT 0)
+set (WAMR_BUILD_LIBC_BUILTIN 0)
+set (WAMR_BUILD_LIB_WASI_THREADS 1)
+
+if (NOT MSVC)
+  set (WAMR_BUILD_LIBC_WASI 1)
+endif ()
+
+if (NOT MSVC)
+  # linker flags
+  if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang"))
+    set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
+  endif ()
+  set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security")
+  if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64")
+    if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang"))
+      set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register")
+    endif ()
+  endif ()
+endif ()
+
+# build out vmlib
+set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
+include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
+
+add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE})
+
+################  application related  ################
+include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
+include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake)
+
+add_executable (inst-context src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE})
+
+check_pie_supported()
+set_target_properties (inst-context PROPERTIES POSITION_INDEPENDENT_CODE ON)
+
+if (APPLE)
+  target_link_libraries (inst-context vmlib -lm -ldl -lpthread)
+else ()
+  target_link_libraries (inst-context vmlib -lm -ldl -lpthread -lrt)
+endif ()

+ 4 - 0
samples/inst-context-threads/README.md

@@ -0,0 +1,4 @@
+The "inst-context" sample project
+=================================
+
+This sample demonstrates module instance context API.

+ 61 - 0
samples/inst-context-threads/build.sh

@@ -0,0 +1,61 @@
+#
+# Copyright (C) 2019 Intel Corporation.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+
+#!/bin/bash
+
+CURR_DIR=$PWD
+WAMR_DIR=${PWD}/../..
+OUT_DIR=${PWD}/out
+
+WASM_APPS=${PWD}/wasm-apps
+
+
+rm -rf ${OUT_DIR}
+mkdir ${OUT_DIR}
+mkdir ${OUT_DIR}/wasm-apps
+
+
+echo "#####################build inst-context project"
+cd ${CURR_DIR}
+mkdir -p cmake_build
+cd cmake_build
+cmake ..
+make -j ${nproc}
+if [ $? != 0 ];then
+    echo "BUILD_FAIL inst-context exit as $?\n"
+    exit 2
+fi
+
+cp -a inst-context ${OUT_DIR}
+
+echo -e "\n"
+
+echo "#####################build wasm apps"
+
+cd ${WASM_APPS}
+
+for i in `ls *.c`
+do
+APP_SRC="$i"
+OUT_FILE=${i%.*}.wasm
+
+# use WAMR SDK to build out the .wasm binary
+# require wasi-sdk with wasi-threads support. (wasi-sdk-20.0 or later)
+/opt/wasi-sdk/bin/clang     \
+        --target=wasm32-wasi-threads \
+        -pthread \
+        -Wl,--import-memory \
+        -Wl,--export-memory \
+        -Wl,--max-memory=655360 \
+        -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC}
+
+
+if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then
+        echo "build ${OUT_FILE} success"
+else
+        echo "build ${OUT_FILE} fail"
+fi
+done
+echo "####################build wasm apps done"

+ 3 - 0
samples/inst-context-threads/run.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+out/inst-context -f out/wasm-apps/testapp.wasm

+ 151 - 0
samples/inst-context-threads/src/main.c

@@ -0,0 +1,151 @@
+
+/*
+ * Copyright (C) 2019 Intel Corporation.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "wasm_export.h"
+#include "bh_read_file.h"
+#include "bh_getopt.h"
+#include "my_context.h"
+
+void
+set_context(wasm_exec_env_t exec_env, int32_t n);
+int32_t
+get_context(wasm_exec_env_t exec_env);
+
+void *my_context_key;
+struct my_context my_context;
+int my_dtor_called;
+
+wasm_module_inst_t module_inst = NULL;
+
+void
+print_usage(void)
+{
+    fprintf(stdout, "Options:\r\n");
+    fprintf(stdout, "  -f [path of wasm file] \n");
+}
+
+void
+my_context_dtor(wasm_module_inst_t inst, void *ctx)
+{
+    printf("%s called\n", __func__);
+    my_dtor_called++;
+    bh_assert(ctx == &my_context);
+    bh_assert(inst == module_inst);
+}
+
+int
+main(int argc, char *argv_main[])
+{
+    static char global_heap_buf[512 * 1024];
+    char *buffer;
+    char error_buf[128];
+    int opt;
+    char *wasm_path = NULL;
+    int exit_code = 1;
+
+    wasm_module_t module = NULL;
+    uint32 buf_size, stack_size = 8092, heap_size = 8092;
+
+    RuntimeInitArgs init_args;
+    memset(&init_args, 0, sizeof(RuntimeInitArgs));
+
+    while ((opt = getopt(argc, argv_main, "hf:")) != -1) {
+        switch (opt) {
+            case 'f':
+                wasm_path = optarg;
+                break;
+            case 'h':
+                print_usage();
+                return 0;
+            case '?':
+                print_usage();
+                return 0;
+        }
+    }
+    if (optind == 1) {
+        print_usage();
+        return 0;
+    }
+
+    // Define an array of NativeSymbol for the APIs to be exported.
+    // Note: the array must be static defined since runtime
+    //            will keep it after registration
+    // For the function signature specifications, goto the link:
+    // https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md
+
+    static NativeSymbol native_symbols[] = {
+        { "set_context", set_context, "(i)", NULL },
+        { "get_context", get_context, "()i", NULL },
+    };
+
+    init_args.mem_alloc_type = Alloc_With_Pool;
+    init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
+    init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
+
+    // Native symbols need below registration phase
+    init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol);
+    init_args.native_module_name = "env";
+    init_args.native_symbols = native_symbols;
+
+    if (!wasm_runtime_full_init(&init_args)) {
+        printf("Init runtime environment failed.\n");
+        return -1;
+    }
+
+    my_context_key = wasm_runtime_create_context_key(my_context_dtor);
+    if (!my_context_key) {
+        printf("wasm_runtime_create_context_key failed.\n");
+        return -1;
+    }
+
+    buffer = bh_read_file_to_buffer(wasm_path, &buf_size);
+
+    if (!buffer) {
+        printf("Open wasm app file [%s] failed.\n", wasm_path);
+        goto fail;
+    }
+
+    module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf,
+                               sizeof(error_buf));
+    if (!module) {
+        printf("Load wasm module failed. error: %s\n", error_buf);
+        goto fail;
+    }
+
+    module_inst = wasm_runtime_instantiate(module, stack_size, heap_size,
+                                           error_buf, sizeof(error_buf));
+
+    if (!module_inst) {
+        printf("Instantiate wasm module failed. error: %s\n", error_buf);
+        goto fail;
+    }
+
+    char *args[] = {
+        "testapp",
+    };
+    wasm_application_execute_main(module_inst, 1, args);
+    const char *exc = wasm_runtime_get_exception(module_inst);
+    if (exc != NULL) {
+        printf("call wasm function calculate failed. error: %s\n", exc);
+        goto fail;
+    }
+
+    exit_code = 0;
+fail:
+    if (module_inst) {
+        bh_assert(my_dtor_called == 0);
+        wasm_runtime_deinstantiate(module_inst);
+        bh_assert(my_dtor_called == 1);
+    }
+    if (module)
+        wasm_runtime_unload(module);
+    if (buffer)
+        BH_FREE(buffer);
+    if (my_context_key)
+        wasm_runtime_destroy_context_key(my_context_key);
+    wasm_runtime_destroy();
+    return exit_code;
+}

+ 11 - 0
samples/inst-context-threads/src/my_context.h

@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2023 Midokura Japan KK.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+struct my_context {
+    int x;
+};
+
+extern void *my_context_key;
+extern struct my_context my_context;

+ 32 - 0
samples/inst-context-threads/src/native_impl.c

@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 Midokura Japan KK.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "wasm_export.h"
+#include "my_context.h"
+
+void
+set_context(wasm_exec_env_t exec_env, int32_t n)
+{
+    wasm_module_inst_t inst = wasm_runtime_get_module_inst(exec_env);
+    printf("%s called on module inst %p\n", __func__, inst);
+    struct my_context *ctx = &my_context;
+    ctx->x = n;
+    wasm_runtime_set_context_spread(inst, my_context_key, ctx);
+}
+
+int32_t
+get_context(wasm_exec_env_t exec_env)
+{
+    wasm_module_inst_t inst = wasm_runtime_get_module_inst(exec_env);
+    printf("%s called on module inst %p\n", __func__, inst);
+    struct my_context *ctx = wasm_runtime_get_context(inst, my_context_key);
+    if (ctx == NULL) {
+        return -1;
+    }
+    return ctx->x;
+}

+ 65 - 0
samples/inst-context-threads/wasm-apps/testapp.c

@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 Midokura Japan KK.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+void
+set_context(int32_t n) __attribute__((import_module("env")))
+__attribute__((import_name("set_context")));
+
+int32_t
+get_context() __attribute__((import_module("env")))
+__attribute__((import_name("get_context")));
+
+void *
+start(void *vp)
+{
+    int32_t v;
+
+    printf("thread started\n");
+
+    printf("confirming the initial state on thread\n");
+    v = get_context();
+    assert(v == -1);
+
+    printf("setting the context on thread\n");
+    set_context(1234);
+
+    printf("confirming the context on thread\n");
+    v = get_context();
+    assert(v == 1234);
+    return NULL;
+}
+
+int
+main()
+{
+    pthread_t t1;
+    int32_t v;
+    int ret;
+
+    printf("confirming the initial state on main\n");
+    v = get_context();
+    assert(v == -1);
+
+    printf("creating a thread\n");
+    ret = pthread_create(&t1, NULL, start, NULL);
+    assert(ret == 0);
+    void *val;
+    ret = pthread_join(t1, &val);
+    assert(ret == 0);
+    printf("joined the thread\n");
+
+    printf("confirming the context propagated from the thread on main\n");
+    v = get_context();
+    assert(v == 1234);
+
+    printf("success\n");
+    return 0;
+}

+ 1 - 0
samples/inst-context/.gitignore

@@ -0,0 +1 @@
+/out/

+ 91 - 0
samples/inst-context/CMakeLists.txt

@@ -0,0 +1,91 @@
+# Copyright (C) 2019 Intel Corporation.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+cmake_minimum_required (VERSION 3.14)
+
+include(CheckPIESupported)
+
+if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows")
+  project (inst-context)
+else()
+  project (inst-context C ASM)
+  enable_language (ASM_MASM)
+endif()
+
+################  runtime settings  ################
+string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM)
+if (APPLE)
+  add_definitions(-DBH_PLATFORM_DARWIN)
+endif ()
+
+# Reset default linker flags
+set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
+
+# WAMR features switch
+
+# Set WAMR_BUILD_TARGET, currently values supported:
+# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]",
+# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]"
+if (NOT DEFINED WAMR_BUILD_TARGET)
+  if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)")
+    set (WAMR_BUILD_TARGET "AARCH64")
+  elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
+    set (WAMR_BUILD_TARGET "RISCV64")
+  elseif (CMAKE_SIZEOF_VOID_P EQUAL 8)
+    # Build as X86_64 by default in 64-bit platform
+    set (WAMR_BUILD_TARGET "X86_64")
+  elseif (CMAKE_SIZEOF_VOID_P EQUAL 4)
+    # Build as X86_32 by default in 32-bit platform
+    set (WAMR_BUILD_TARGET "X86_32")
+  else ()
+    message(SEND_ERROR "Unsupported build target platform!")
+  endif ()
+endif ()
+
+if (NOT CMAKE_BUILD_TYPE)
+  set (CMAKE_BUILD_TYPE Debug)
+endif ()
+
+set (WAMR_BUILD_INTERP 1)
+set (WAMR_BUILD_AOT 1)
+set (WAMR_BUILD_JIT 0)
+set (WAMR_BUILD_LIBC_BUILTIN 1)
+
+if (NOT MSVC)
+  set (WAMR_BUILD_LIBC_WASI 1)
+endif ()
+
+if (NOT MSVC)
+  # linker flags
+  if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang"))
+    set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
+  endif ()
+  set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security")
+  if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64")
+    if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang"))
+      set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register")
+    endif ()
+  endif ()
+endif ()
+
+# build out vmlib
+set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
+include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
+
+add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE})
+
+################  application related  ################
+include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
+include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake)
+
+add_executable (inst-context src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE})
+
+check_pie_supported()
+set_target_properties (inst-context PROPERTIES POSITION_INDEPENDENT_CODE ON)
+
+if (APPLE)
+  target_link_libraries (inst-context vmlib -lm -ldl -lpthread)
+else ()
+  target_link_libraries (inst-context vmlib -lm -ldl -lpthread -lrt)
+endif ()

+ 4 - 0
samples/inst-context/README.md

@@ -0,0 +1,4 @@
+The "inst-context" sample project
+=================================
+
+This sample demonstrates module instance context API.

+ 63 - 0
samples/inst-context/build.sh

@@ -0,0 +1,63 @@
+#
+# Copyright (C) 2019 Intel Corporation.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+
+#!/bin/bash
+
+CURR_DIR=$PWD
+WAMR_DIR=${PWD}/../..
+OUT_DIR=${PWD}/out
+
+WASM_APPS=${PWD}/wasm-apps
+
+
+rm -rf ${OUT_DIR}
+mkdir ${OUT_DIR}
+mkdir ${OUT_DIR}/wasm-apps
+
+
+echo "#####################build inst-context project"
+cd ${CURR_DIR}
+mkdir -p cmake_build
+cd cmake_build
+cmake ..
+make -j ${nproc}
+if [ $? != 0 ];then
+    echo "BUILD_FAIL inst-context exit as $?\n"
+    exit 2
+fi
+
+cp -a inst-context ${OUT_DIR}
+
+echo -e "\n"
+
+echo "#####################build wasm apps"
+
+cd ${WASM_APPS}
+
+for i in `ls *.c`
+do
+APP_SRC="$i"
+OUT_FILE=${i%.*}.wasm
+
+# use WAMR SDK to build out the .wasm binary
+/opt/wasi-sdk/bin/clang     \
+        --target=wasm32 -O0 -z stack-size=4096 -Wl,--initial-memory=65536 \
+        --sysroot=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot  \
+        -Wl,--allow-undefined-file=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt \
+        -Wl,--strip-all,--no-entry -nostdlib \
+        -Wl,--export=generate_float \
+        -Wl,--export=float_to_string \
+        -Wl,--export=calculate\
+        -Wl,--allow-undefined \
+        -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC}
+
+
+if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then
+        echo "build ${OUT_FILE} success"
+else
+        echo "build ${OUT_FILE} fail"
+fi
+done
+echo "####################build wasm apps done"

+ 3 - 0
samples/inst-context/run.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+out/inst-context -f out/wasm-apps/testapp.wasm

+ 166 - 0
samples/inst-context/src/main.c

@@ -0,0 +1,166 @@
+
+/*
+ * Copyright (C) 2019 Intel Corporation.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "wasm_export.h"
+#include "bh_read_file.h"
+#include "bh_getopt.h"
+#include "my_context.h"
+
+int32_t
+add_native(int32_t n);
+void *my_context_key;
+struct my_context my_context;
+int my_dtor_called;
+
+wasm_module_inst_t module_inst = NULL;
+
+void
+print_usage(void)
+{
+    fprintf(stdout, "Options:\r\n");
+    fprintf(stdout, "  -f [path of wasm file] \n");
+}
+
+void
+my_context_dtor(wasm_module_inst_t inst, void *ctx)
+{
+    printf("%s called\n", __func__);
+    my_dtor_called++;
+    bh_assert(ctx == &my_context);
+    bh_assert(inst == module_inst);
+}
+
+int
+main(int argc, char *argv_main[])
+{
+    static char global_heap_buf[512 * 1024];
+    char *buffer;
+    char error_buf[128];
+    int opt;
+    char *wasm_path = NULL;
+
+    wasm_module_t module = NULL;
+    wasm_exec_env_t exec_env = NULL;
+    uint32 buf_size, stack_size = 8092, heap_size = 8092;
+
+    RuntimeInitArgs init_args;
+    memset(&init_args, 0, sizeof(RuntimeInitArgs));
+
+    while ((opt = getopt(argc, argv_main, "hf:")) != -1) {
+        switch (opt) {
+            case 'f':
+                wasm_path = optarg;
+                break;
+            case 'h':
+                print_usage();
+                return 0;
+            case '?':
+                print_usage();
+                return 0;
+        }
+    }
+    if (optind == 1) {
+        print_usage();
+        return 0;
+    }
+
+    // Define an array of NativeSymbol for the APIs to be exported.
+    // Note: the array must be static defined since runtime
+    //            will keep it after registration
+    // For the function signature specifications, goto the link:
+    // https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md
+
+    static NativeSymbol native_symbols[] = { { "add_native", add_native, "(i)i",
+                                               NULL } };
+
+    init_args.mem_alloc_type = Alloc_With_Pool;
+    init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
+    init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
+
+    // Native symbols need below registration phase
+    init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol);
+    init_args.native_module_name = "env";
+    init_args.native_symbols = native_symbols;
+
+    if (!wasm_runtime_full_init(&init_args)) {
+        printf("Init runtime environment failed.\n");
+        return -1;
+    }
+
+    my_context_key = wasm_runtime_create_context_key(my_context_dtor);
+    if (!my_context_key) {
+        printf("wasm_runtime_create_context_key failed.\n");
+        return -1;
+    }
+
+    buffer = bh_read_file_to_buffer(wasm_path, &buf_size);
+
+    if (!buffer) {
+        printf("Open wasm app file [%s] failed.\n", wasm_path);
+        goto fail;
+    }
+
+    module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf,
+                               sizeof(error_buf));
+    if (!module) {
+        printf("Load wasm module failed. error: %s\n", error_buf);
+        goto fail;
+    }
+
+    module_inst = wasm_runtime_instantiate(module, stack_size, heap_size,
+                                           error_buf, sizeof(error_buf));
+
+    if (!module_inst) {
+        printf("Instantiate wasm module failed. error: %s\n", error_buf);
+        goto fail;
+    }
+
+    my_context.x = 100;
+    wasm_runtime_set_context(module_inst, my_context_key, &my_context);
+
+    exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
+    if (!exec_env) {
+        printf("Create wasm execution environment failed.\n");
+        goto fail;
+    }
+
+    wasm_function_inst_t func3 =
+        wasm_runtime_lookup_function(module_inst, "calculate", NULL);
+    if (!func3) {
+        printf("The wasm function calculate is not found.\n");
+        goto fail;
+    }
+
+    uint32_t argv3[1] = { 3 };
+    if (wasm_runtime_call_wasm(exec_env, func3, 1, argv3)) {
+        uint32_t result = *(uint32_t *)argv3;
+        printf("Native finished calling wasm function: calculate, return: %d\n",
+               result);
+        bh_assert(result == 103); /* argv3[0] + my_context.x */
+    }
+    else {
+        printf("call wasm function calculate failed. error: %s\n",
+               wasm_runtime_get_exception(module_inst));
+        goto fail;
+    }
+
+fail:
+    if (exec_env)
+        wasm_runtime_destroy_exec_env(exec_env);
+    if (module_inst) {
+        bh_assert(my_dtor_called == 0);
+        wasm_runtime_deinstantiate(module_inst);
+        bh_assert(my_dtor_called == 1);
+    }
+    if (module)
+        wasm_runtime_unload(module);
+    if (buffer)
+        BH_FREE(buffer);
+    if (my_context_key)
+        wasm_runtime_destroy_context_key(my_context_key);
+    wasm_runtime_destroy();
+    return 0;
+}

+ 10 - 0
samples/inst-context/src/my_context.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (C) 2023 Midokura Japan KK.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+struct my_context {
+    int x;
+};
+
+extern void *my_context_key;

+ 15 - 0
samples/inst-context/src/native_impl.c

@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2023 Midokura Japan KK.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "wasm_export.h"
+#include "my_context.h"
+
+int32_t
+add_native(wasm_exec_env_t exec_env, int32_t n)
+{
+    wasm_module_inst_t inst = wasm_runtime_get_module_inst(exec_env);
+    struct my_context *ctx = wasm_runtime_get_context(inst, my_context_key);
+    return n + ctx->x;
+}

+ 19 - 0
samples/inst-context/wasm-apps/testapp.c

@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 Intel Corporation.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+int32_t
+add_native(int32_t n);
+
+int32_t
+calculate(int32_t n)
+{
+    printf("calling into WASM function: %s\n", __FUNCTION__);
+    return add_native(n);
+}

+ 1 - 0
wamr-compiler/CMakeLists.txt

@@ -45,6 +45,7 @@ add_definitions(-DWASM_ENABLE_DUMP_CALL_STACK=1)
 add_definitions(-DWASM_ENABLE_PERF_PROFILING=1)
 add_definitions(-DWASM_ENABLE_LOAD_CUSTOM_SECTION=1)
 add_definitions(-DWASM_ENABLE_LIB_WASI_THREADS=1)
+add_definitions(-DWASM_ENABLE_MODULE_INST_CONTEXT=1)
 
 if (WAMR_BUILD_LLVM_LEGACY_PM EQUAL 1)
   add_definitions(-DWASM_ENABLE_LLVM_LEGACY_PM=1)