Enable shared memory && add pthread support (#282)

This commit is contained in:
Xu Jun
2020-06-15 19:04:04 +08:00
committed by GitHub
parent f4d4d69736
commit d98ab63e5c
41 changed files with 3081 additions and 289 deletions

View File

@ -0,0 +1,13 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
set (LIB_PTHREAD_DIR ${CMAKE_CURRENT_LIST_DIR})
add_definitions (-DWASM_ENABLE_LIB_PTHREAD=1)
include_directories(${LIB_PTHREAD_DIR})
file (GLOB source_all ${LIB_PTHREAD_DIR}/*.c)
set (LIB_PTHREAD_SOURCE ${source_all})

View File

@ -0,0 +1,731 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "bh_common.h"
#include "bh_log.h"
#include "wasm_export.h"
#include "../interpreter/wasm.h"
#include "../common/wasm_runtime_common.h"
#include "thread_manager.h"
#define get_module(exec_env) \
wasm_exec_env_get_module(exec_env)
#define get_module_inst(exec_env) \
wasm_runtime_get_module_inst(exec_env)
#define get_thread_arg(exec_env) \
wasm_exec_env_get_thread_arg(exec_env)
#define get_wasi_ctx(module_inst) \
wasm_runtime_get_wasi_ctx(module_inst)
#define validate_app_addr(offset, size) \
wasm_runtime_validate_app_addr(module_inst, offset, size)
#define validate_native_addr(addr, size) \
wasm_runtime_validate_native_addr(module_inst, addr, size)
#define addr_app_to_native(offset) \
wasm_runtime_addr_app_to_native(module_inst, offset)
#define addr_native_to_app(ptr) \
wasm_runtime_addr_native_to_app(module_inst, ptr)
extern bool
wasm_runtime_call_indirect(wasm_exec_env_t exec_env,
uint32 element_indices,
uint32 argc, uint32 argv[]);
enum {
T_THREAD,
T_MUTEX,
T_COND,
};
enum thread_status_t {
THREAD_INIT,
THREAD_RUNNING,
THREAD_CANCELLED,
THREAD_EXIT,
};
enum mutex_status_t {
MUTEX_CREATED,
MUTEX_DESTROYED,
};
enum cond_status_t {
COND_CREATED,
COND_DESTROYED,
};
typedef struct ClusterInfoNode {
bh_list_link l;
WASMCluster *cluster;
HashMap *thread_info_map;
} ClusterInfoNode;
typedef struct ThreadInfoNode {
wasm_exec_env_t parent_exec_env;
wasm_exec_env_t exec_env;
/* the id returned to app */
uint32 handle;
/* type can be [THREAD | MUTEX | CONDITION] */
uint32 type;
/* Thread status, this variable should be volatile
as its value may be changed in different threads */
volatile uint32 status;
union {
korp_tid thread;
korp_mutex *mutex;
korp_cond *cond;
} u;
} ThreadInfoNode;
typedef struct {
ThreadInfoNode *info_node;
/* table elem index of the app's entry function */
uint32 elem_index;
/* arg of the app's entry function */
void *arg;
wasm_module_inst_t module_inst;
} ThreadRoutineArgs;
static bh_list cluster_info_list;
static korp_mutex pthread_global_lock;
static uint32 handle_id = 1;
static void
lib_pthread_destroy_callback(WASMCluster *cluster);
static uint32
thread_handle_hash(void *handle)
{
return (uint32)(uintptr_t)handle;
}
static bool
thread_handle_equal(void *h1, void *h2)
{
return (uint32)(uintptr_t)h1 == (uint32)(uintptr_t)h2 ? true : false;
}
static void
thread_info_destroy(void *node)
{
ThreadInfoNode *info_node = (ThreadInfoNode *)node;
ThreadRoutineArgs *args;
pthread_mutex_lock(&pthread_global_lock);
if (info_node->type == T_THREAD) {
args = get_thread_arg(info_node->exec_env);
if (args) {
wasm_runtime_free(args);
}
}
else if (info_node->type == T_MUTEX) {
if (info_node->status != MUTEX_DESTROYED)
os_mutex_destroy(info_node->u.mutex);
wasm_runtime_free(info_node->u.mutex);
}
else if (info_node->type == T_COND) {
if (info_node->status != COND_DESTROYED)
os_cond_destroy(info_node->u.cond);
wasm_runtime_free(info_node->u.cond);
}
wasm_runtime_free(info_node);
pthread_mutex_unlock(&pthread_global_lock);
}
bool
lib_pthread_init()
{
if (0 != os_mutex_init(&pthread_global_lock))
return false;
bh_list_init(&cluster_info_list);
if (!wasm_cluster_register_destroy_callback(
lib_pthread_destroy_callback)) {
os_mutex_destroy(&pthread_global_lock);
return false;
}
return true;
}
void
lib_pthread_destroy()
{
os_mutex_destroy(&pthread_global_lock);
}
static ClusterInfoNode*
get_cluster_info(WASMCluster *cluster)
{
ClusterInfoNode *node;
os_mutex_lock(&pthread_global_lock);
node = bh_list_first_elem(&cluster_info_list);
while (node) {
if (cluster == node->cluster) {
os_mutex_unlock(&pthread_global_lock);
return node;
}
node = bh_list_elem_next(node);
}
os_mutex_unlock(&pthread_global_lock);
return NULL;
}
static ClusterInfoNode*
create_cluster_info(WASMCluster *cluster)
{
ClusterInfoNode *node;
bh_list_status ret;
if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) {
return NULL;
}
node->cluster = cluster;
if (!(node->thread_info_map =
bh_hash_map_create(32, true,
(HashFunc)thread_handle_hash,
(KeyEqualFunc)thread_handle_equal,
NULL,
thread_info_destroy))) {
wasm_runtime_free(node);
return NULL;
}
os_mutex_lock(&pthread_global_lock);
ret = bh_list_insert(&cluster_info_list, node);
bh_assert(ret == 0);
os_mutex_unlock(&pthread_global_lock);
(void)ret;
return node;
}
static bool
destroy_cluster_info(WASMCluster *cluster)
{
ClusterInfoNode *node = get_cluster_info(cluster);
if (node) {
bh_hash_map_destroy(node->thread_info_map);
os_mutex_lock(&pthread_global_lock);
bh_list_remove(&cluster_info_list, node);
wasm_runtime_free(node);
os_mutex_unlock(&pthread_global_lock);
return true;
}
return false;
}
static void
lib_pthread_destroy_callback(WASMCluster *cluster)
{
destroy_cluster_info(cluster);
}
static void
delete_thread_info_node(ThreadInfoNode *thread_info)
{
ClusterInfoNode *node;
bool ret;
WASMCluster *cluster =
wasm_exec_env_get_cluster(thread_info->exec_env);
if ((node = get_cluster_info(cluster))) {
ret = bh_hash_map_remove(node->thread_info_map,
(void *)(uintptr_t)thread_info->handle,
NULL, NULL);
(void)ret;
}
thread_info_destroy(thread_info);
}
static bool
append_thread_info_node(ThreadInfoNode *thread_info)
{
ClusterInfoNode *node;
WASMCluster *cluster =
wasm_exec_env_get_cluster(thread_info->exec_env);
if (!(node = get_cluster_info(cluster))) {
if (!(node = create_cluster_info(cluster))) {
return false;
}
}
if (!bh_hash_map_insert(node->thread_info_map,
(void *)(uintptr_t)thread_info->handle,
thread_info)) {
return false;
}
return true;
}
static ThreadInfoNode*
get_thread_info(wasm_exec_env_t exec_env, uint32 handle)
{
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
ClusterInfoNode *info = get_cluster_info(cluster);
return bh_hash_map_find(info->thread_info_map, (void *)(uintptr_t)handle);
}
static uint32
allocate_handle()
{
uint32 id;
os_mutex_lock(&pthread_global_lock);
id = handle_id++;
os_mutex_unlock(&pthread_global_lock);
return id;
}
static void*
pthread_start_routine(void *arg)
{
wasm_exec_env_t exec_env = (wasm_exec_env_t)arg;
wasm_exec_env_t parent_exec_env;
wasm_module_inst_t module_inst = get_module_inst(exec_env);
ThreadRoutineArgs *routine_args = exec_env->thread_arg;
ThreadInfoNode *info_node = routine_args->info_node;
uint32 argv[1];
parent_exec_env = info_node->parent_exec_env;
os_mutex_lock(&parent_exec_env->wait_lock);
info_node->exec_env = exec_env;
info_node->u.thread = exec_env->handle;
if (!append_thread_info_node(info_node)) {
wasm_runtime_deinstantiate_internal(module_inst, true);
delete_thread_info_node(info_node);
os_cond_signal(&parent_exec_env->wait_cond);
os_mutex_unlock(&parent_exec_env->wait_lock);
return NULL;
}
info_node->status = THREAD_RUNNING;
os_cond_signal(&parent_exec_env->wait_cond);
os_mutex_unlock(&parent_exec_env->wait_lock);
if (!validate_native_addr(routine_args->arg, sizeof(uint32))) {
/* If there are exceptions, copy the exception to
all other instance in this cluster */
wasm_cluster_spread_exception(exec_env);
wasm_runtime_deinstantiate_internal(module_inst, true);
delete_thread_info_node(info_node);
return NULL;
}
argv[0] = addr_native_to_app(routine_args->arg);
if(!wasm_runtime_call_indirect(exec_env,
routine_args->elem_index,
1, argv)) {
wasm_cluster_spread_exception(exec_env);
}
/* routine exit, destroy instance */
wasm_runtime_deinstantiate_internal(module_inst, true);
info_node->status = THREAD_EXIT;
delete_thread_info_node(info_node);
return (void *)(uintptr_t)argv[0];
}
static int
pthread_create_wrapper(wasm_exec_env_t exec_env,
uint32 *thread, /* thread_handle */
const void *attr, /* not supported */
uint32 elem_index, /* entry function */
void *arg) /* arguments buffer */
{
wasm_module_t module = get_module(exec_env);
wasm_module_inst_t new_module_inst = NULL;
ThreadInfoNode *info_node = NULL;
ThreadRoutineArgs *routine_args = NULL;
uint32 thread_handle;
int32 ret = -1;
#if WASM_ENABLE_LIBC_WASI != 0
wasm_module_inst_t module_inst = get_module_inst(exec_env);
WASIContext *wasi_ctx = get_wasi_ctx(module_inst);
#endif
if (!(new_module_inst =
wasm_runtime_instantiate_internal(module, true, 8192, 0,
NULL, 0)))
return -1;
#if WASM_ENABLE_LIBC_WASI != 0
if (wasi_ctx)
wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx);
#endif
if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode))))
goto fail;
memset(info_node, 0, sizeof(ThreadInfoNode));
thread_handle = allocate_handle();
info_node->parent_exec_env = exec_env;
info_node->handle = thread_handle;
info_node->type = T_THREAD;
info_node->status = THREAD_INIT;
if (!(routine_args = wasm_runtime_malloc(sizeof(ThreadRoutineArgs))))
goto fail;
routine_args->arg = arg;
routine_args->elem_index = elem_index;
routine_args->info_node = info_node;
routine_args->module_inst = new_module_inst;
os_mutex_lock(&exec_env->wait_lock);
ret = wasm_cluster_create_thread(exec_env, new_module_inst,
pthread_start_routine,
(void *)routine_args);
if (ret != 0) {
os_mutex_unlock(&exec_env->wait_lock);
goto fail;
}
/* Wait for the thread routine to assign the exec_env to
thread_info_node, otherwise the exec_env in the thread
info node may be NULL in the next pthread API call */
os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock);
os_mutex_unlock(&exec_env->wait_lock);
if (thread)
*thread = thread_handle;
return 0;
fail:
if (new_module_inst)
wasm_runtime_deinstantiate_internal(new_module_inst, true);
if (info_node)
wasm_runtime_free(info_node);
if (routine_args)
wasm_runtime_free(routine_args);
return ret;
}
static int32
pthread_join_wrapper(wasm_exec_env_t exec_env, uint32 thread,
int32 retval_offset) /* void **retval */
{
uint32 *ret;
int32 join_ret;
void **retval;
ThreadInfoNode *node;
wasm_module_inst_t module_inst;
wasm_exec_env_t target_exec_env;
node = get_thread_info(exec_env, thread);
if (!node) {
/* The thread has exited, return 0 to app */
return 0;
}
target_exec_env = node->exec_env;
bh_assert(target_exec_env != NULL);
module_inst = get_module_inst(target_exec_env);
/* validate addr before join thread, otherwise
the module_inst may be freed */
if (!validate_app_addr(retval_offset, sizeof(uint32))) {
/* Join failed, but we don't want to terminate all threads,
do not spread exception here */
wasm_runtime_set_exception(module_inst, NULL);
return -1;
}
retval = (void **)addr_app_to_native(retval_offset);
join_ret = wasm_cluster_join_thread(target_exec_env, (void **)&ret);
if (retval_offset != 0)
*retval = (void*)ret;
return join_ret;
}
static int32
pthread_detach_wrapper(wasm_exec_env_t exec_env, uint32 thread)
{
ThreadInfoNode *node;
wasm_exec_env_t target_exec_env;
node = get_thread_info(exec_env, thread);
if (!node)
return 0;
target_exec_env = node->exec_env;
bh_assert(target_exec_env != NULL);
return wasm_cluster_detach_thread(target_exec_env);
}
static int32
pthread_cancel_wrapper(wasm_exec_env_t exec_env, uint32 thread)
{
ThreadInfoNode *node;
wasm_exec_env_t target_exec_env;
node = get_thread_info(exec_env, thread);
if (!node)
return 0;
target_exec_env = node->exec_env;
bh_assert(target_exec_env != NULL);
return wasm_cluster_cancel_thread(target_exec_env);
}
static int32
pthread_self_wrapper(wasm_exec_env_t exec_env)
{
ThreadRoutineArgs *args = get_thread_arg(exec_env);
/* If thread_arg is NULL, it's the exec_env of the main thread,
return id 0 to app */
if (!args)
return 0;
return args->info_node->handle;
}
static void
pthread_exit_wrapper(wasm_exec_env_t exec_env, int32 retval_offset)
{
wasm_module_inst_t module_inst = get_module_inst(exec_env);
ThreadRoutineArgs *args = get_thread_arg(exec_env);
/* Currently exit main thread is not allowed */
if (!args)
return;
/* routine exit, destroy instance */
wasm_runtime_deinstantiate_internal(module_inst, true);
delete_thread_info_node(args->info_node);
wasm_cluster_exit_thread(exec_env, (void *)(uintptr_t)retval_offset);
}
static int32
pthread_mutex_init_wrapper(wasm_exec_env_t exec_env, uint32 *mutex, void *attr)
{
korp_mutex *pmutex;
ThreadInfoNode *info_node;
if (!(pmutex = wasm_runtime_malloc(sizeof(korp_mutex)))) {
return -1;
}
if (os_mutex_init(pmutex) != 0) {
goto fail1;
}
if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode))))
goto fail2;
memset(info_node, 0, sizeof(ThreadInfoNode));
info_node->exec_env = exec_env;
info_node->handle = allocate_handle();
info_node->type = T_MUTEX;
info_node->u.mutex = pmutex;
info_node->status = MUTEX_CREATED;
if (!append_thread_info_node(info_node))
goto fail3;
/* Return the mutex handle to app */
if (mutex)
*(uint32*)mutex = info_node->handle;
return 0;
fail3:
delete_thread_info_node(info_node);
fail2:
os_mutex_destroy(pmutex);
fail1:
wasm_runtime_free(pmutex);
return -1;
}
static int32
pthread_mutex_lock_wrapper(wasm_exec_env_t exec_env, uint32 *mutex)
{
ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex);
if (!info_node || info_node->type != T_MUTEX)
return -1;
return os_mutex_lock(info_node->u.mutex);
}
static int32
pthread_mutex_unlock_wrapper(wasm_exec_env_t exec_env, uint32 *mutex)
{
ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex);
if (!info_node || info_node->type != T_MUTEX)
return -1;
return os_mutex_unlock(info_node->u.mutex);
}
static int32
pthread_mutex_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *mutex)
{
int32 ret_val;
ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex);
if (!info_node || info_node->type != T_MUTEX)
return -1;
ret_val = os_mutex_destroy(info_node->u.mutex);
info_node->status = MUTEX_DESTROYED;
delete_thread_info_node(info_node);
return ret_val;
}
static int32
pthread_cond_init_wrapper(wasm_exec_env_t exec_env, uint32 *cond, void *attr)
{
korp_cond *pcond;
ThreadInfoNode *info_node;
if (!(pcond = wasm_runtime_malloc(sizeof(korp_cond)))) {
return -1;
}
if (os_cond_init(pcond) != 0) {
goto fail1;
}
if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode))))
goto fail2;
memset(info_node, 0, sizeof(ThreadInfoNode));
info_node->exec_env = exec_env;
info_node->handle = allocate_handle();
info_node->type = T_COND;
info_node->u.cond = pcond;
info_node->status = COND_CREATED;
if (!append_thread_info_node(info_node))
goto fail3;
/* Return the cond handle to app */
if (cond)
*(uint32*)cond = info_node->handle;
return 0;
fail3:
delete_thread_info_node(info_node);
fail2:
os_cond_destroy(pcond);
fail1:
wasm_runtime_free(pcond);
return -1;
}
static int32
pthread_cond_wait_wrapper(wasm_exec_env_t exec_env, uint32 *cond, uint32 *mutex)
{
ThreadInfoNode *cond_info_node, *mutex_info_node;
cond_info_node = get_thread_info(exec_env, *cond);
if (!cond_info_node || cond_info_node->type != T_COND)
return -1;
mutex_info_node = get_thread_info(exec_env, *mutex);
if (!mutex_info_node || mutex_info_node->type != T_MUTEX)
return -1;
return os_cond_wait(cond_info_node->u.cond, mutex_info_node->u.mutex);
}
/* Currently we don't support struct timespec in built-in libc,
so the pthread_cond_timedwait use useconds instead
*/
static int32
pthread_cond_timedwait_wrapper(wasm_exec_env_t exec_env, uint32 *cond,
uint32 *mutex, uint32 useconds)
{
ThreadInfoNode *cond_info_node, *mutex_info_node;
cond_info_node = get_thread_info(exec_env, *cond);
if (!cond_info_node || cond_info_node->type != T_COND)
return -1;
mutex_info_node = get_thread_info(exec_env, *mutex);
if (!mutex_info_node || mutex_info_node->type != T_MUTEX)
return -1;
return os_cond_reltimedwait(cond_info_node->u.cond,
mutex_info_node->u.mutex, useconds);
}
static int32
pthread_cond_signal_wrapper(wasm_exec_env_t exec_env, uint32 *cond)
{
ThreadInfoNode* info_node = get_thread_info(exec_env, *cond);
if (!info_node || info_node->type != T_COND)
return -1;
return os_cond_signal(info_node->u.cond);
}
static int32
pthread_cond_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *cond)
{
int32 ret_val;
ThreadInfoNode* info_node = get_thread_info(exec_env, *cond);
if (!info_node || info_node->type != T_COND)
return -1;
ret_val = os_cond_destroy(info_node->u.cond);
info_node->status = COND_DESTROYED;
delete_thread_info_node(info_node);
return ret_val;
}
#define REG_NATIVE_FUNC(func_name, signature) \
{ #func_name, func_name##_wrapper, signature, NULL }
static NativeSymbol native_symbols_lib_pthread[] = {
REG_NATIVE_FUNC(pthread_create, "(**i*)i"),
REG_NATIVE_FUNC(pthread_join, "(ii)i"),
REG_NATIVE_FUNC(pthread_detach, "(i)i"),
REG_NATIVE_FUNC(pthread_cancel, "(i)i"),
REG_NATIVE_FUNC(pthread_self, "()i"),
REG_NATIVE_FUNC(pthread_exit, "(i)"),
REG_NATIVE_FUNC(pthread_mutex_init, "(**)i"),
REG_NATIVE_FUNC(pthread_mutex_lock, "(*)i"),
REG_NATIVE_FUNC(pthread_mutex_unlock, "(*)i"),
REG_NATIVE_FUNC(pthread_mutex_destroy, "(*)i"),
REG_NATIVE_FUNC(pthread_cond_init, "(**)i"),
REG_NATIVE_FUNC(pthread_cond_wait, "(**)i"),
REG_NATIVE_FUNC(pthread_cond_timedwait, "(**i)i"),
REG_NATIVE_FUNC(pthread_cond_signal, "(*)i"),
REG_NATIVE_FUNC(pthread_cond_destroy, "(*)i"),
};
uint32
get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis)
{
*p_lib_pthread_apis = native_symbols_lib_pthread;
return sizeof(native_symbols_lib_pthread) / sizeof(NativeSymbol);
}

View File

@ -1131,9 +1131,11 @@ typedef struct WASMNativeGlobalDef {
} WASMNativeGlobalDef;
static WASMNativeGlobalDef native_global_defs[] = {
#if WASM_ENABLE_SPEC_TEST != 0
{ "spectest", "global_i32", .global_data.i32 = 666 },
{ "spectest", "global_f32", .global_data.f32 = 666.6 },
{ "spectest", "global_f64", .global_data.f64 = 666.6 },
#endif
{ "test", "global-i32", .global_data.i32 = 0 },
{ "test", "global-f32", .global_data.f32 = 0 },
{ "env", "STACKTOP", .global_data.u32 = 0 },

View File

@ -58,6 +58,8 @@ static struct fd_table *
wasi_ctx_get_curfds(wasm_module_inst_t module_inst,
wasi_ctx_t wasi_ctx)
{
if (!wasi_ctx)
return NULL;
return (struct fd_table *)
wasm_runtime_addr_app_to_native(module_inst,
wasi_ctx->curfds_offset);
@ -67,6 +69,8 @@ static struct argv_environ_values *
wasi_ctx_get_argv_environ(wasm_module_inst_t module_inst,
wasi_ctx_t wasi_ctx)
{
if (!wasi_ctx)
return NULL;
return (struct argv_environ_values *)
wasm_runtime_addr_app_to_native(module_inst,
wasi_ctx->argv_environ_offset);
@ -76,6 +80,8 @@ static struct fd_prestats *
wasi_ctx_get_prestats(wasm_module_inst_t module_inst,
wasi_ctx_t wasi_ctx)
{
if (!wasi_ctx)
return NULL;
return (struct fd_prestats *)
wasm_runtime_addr_app_to_native(module_inst,
wasi_ctx->prestats_offset);
@ -93,6 +99,9 @@ wasi_args_get(wasm_exec_env_t exec_env, int32 *argv_offsets, char *argv_buf)
uint64 total_size;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
err = wasmtime_ssp_args_sizes_get(argv_environ, &argc, &argv_buf_size);
if (err)
return err;
@ -133,6 +142,9 @@ wasi_args_sizes_get(wasm_exec_env_t exec_env,
size_t argc, argv_buf_size;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(argc_app, sizeof(uint32))
|| !validate_native_addr(argv_buf_size_app, sizeof(uint32)))
return (wasi_errno_t)-1;
@ -191,6 +203,9 @@ wasi_environ_get(wasm_exec_env_t exec_env,
char **environs;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
err = wasmtime_ssp_environ_sizes_get(argv_environ,
&environ_count, &environ_buf_size);
if (err)
@ -234,6 +249,9 @@ wasi_environ_sizes_get(wasm_exec_env_t exec_env,
size_t environ_count, environ_buf_size;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(environ_count_app, sizeof(uint32))
|| !validate_native_addr(environ_buf_size_app, sizeof(uint32)))
return (wasi_errno_t)-1;
@ -259,6 +277,9 @@ wasi_fd_prestat_get(wasm_exec_env_t exec_env,
wasi_prestat_t prestat;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(prestat_app, sizeof(wasi_prestat_app_t)))
return (wasi_errno_t)-1;
@ -279,6 +300,9 @@ wasi_fd_prestat_dir_name(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_prestat_dir_name(prestats,
fd, path, path_len);
}
@ -291,6 +315,9 @@ wasi_fd_close(wasm_exec_env_t exec_env, wasi_fd_t fd)
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_close(curfds, prestats, fd);
}
@ -301,6 +328,9 @@ wasi_fd_datasync(wasm_exec_env_t exec_env, wasi_fd_t fd)
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_datasync(curfds, fd);
}
@ -319,6 +349,9 @@ wasi_fd_pread(wasm_exec_env_t exec_env,
uint32 i;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
total_size = sizeof(iovec_app_t) * (uint64)iovs_len;
if (!validate_native_addr(nread_app, (uint32)sizeof(uint32))
|| total_size >= UINT32_MAX
@ -371,6 +404,9 @@ wasi_fd_pwrite(wasm_exec_env_t exec_env,
uint32 i;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
total_size = sizeof(iovec_app_t) * (uint64)iovs_len;
if (!validate_native_addr(nwritten_app, (uint32)sizeof(uint32))
|| total_size >= UINT32_MAX
@ -423,6 +459,9 @@ wasi_fd_read(wasm_exec_env_t exec_env,
int32 mem;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
total_size = sizeof(iovec_app_t) * (uint64)iovs_len;
if (!validate_native_addr(nread_app, (uint32)sizeof(uint32))
|| total_size >= UINT32_MAX
@ -468,6 +507,9 @@ wasi_fd_renumber(wasm_exec_env_t exec_env, wasi_fd_t from, wasi_fd_t to)
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_renumber(curfds, prestats, from, to);
}
@ -480,6 +522,9 @@ wasi_fd_seek(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(newoffset, sizeof(wasi_filesize_t)))
return (wasi_errno_t)-1;
@ -494,6 +539,9 @@ wasi_fd_tell(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(newoffset, sizeof(wasi_filesize_t)))
return (wasi_errno_t)-1;
@ -510,6 +558,9 @@ wasi_fd_fdstat_get(wasm_exec_env_t exec_env,
wasi_fdstat_t fdstat;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(fdstat_app, sizeof(wasi_fdstat_t)))
return (wasi_errno_t)-1;
@ -529,6 +580,9 @@ wasi_fd_fdstat_set_flags(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_fdstat_set_flags(curfds, fd, flags);
}
@ -542,6 +596,9 @@ wasi_fd_fdstat_set_rights(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_fdstat_set_rights(curfds, fd,
fs_rights_base, fs_rights_inheriting);
}
@ -553,6 +610,9 @@ wasi_fd_sync(wasm_exec_env_t exec_env, wasi_fd_t fd)
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_sync(curfds, fd);
}
@ -571,6 +631,9 @@ wasi_fd_write(wasm_exec_env_t exec_env, wasi_fd_t fd,
uint32 i;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
total_size = sizeof(iovec_app_t) * (uint64)iovs_len;
if (!validate_native_addr(nwritten_app, (uint32)sizeof(uint32))
|| total_size >= UINT32_MAX
@ -619,6 +682,9 @@ wasi_fd_advise(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_advise(curfds, fd, offset, len, advice);
}
@ -632,6 +698,9 @@ wasi_fd_allocate(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_allocate(curfds, fd, offset, len);
}
@ -643,6 +712,9 @@ wasi_path_create_directory(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_path_create_directory(curfds, fd,
path, path_len);
}
@ -660,6 +732,9 @@ wasi_path_link(wasm_exec_env_t exec_env,
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_path_link(curfds, prestats,
old_fd, old_flags, old_path, old_path_len,
new_fd, new_path, new_path_len);
@ -682,6 +757,9 @@ wasi_path_open(wasm_exec_env_t exec_env,
wasi_fd_t fd = -1; /* set fd_app -1 if path open failed */
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(fd_app, sizeof(wasi_fd_t)))
return (wasi_errno_t)-1;
@ -711,6 +789,9 @@ wasi_fd_readdir(wasm_exec_env_t exec_env,
size_t bufused;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(bufused_app, sizeof(uint32)))
return (wasi_errno_t)-1;
@ -736,6 +817,9 @@ wasi_path_readlink(wasm_exec_env_t exec_env,
size_t bufused;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(bufused_app, sizeof(uint32)))
return (wasi_errno_t)-1;
@ -758,6 +842,9 @@ wasi_path_rename(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_path_rename(curfds,
old_fd, old_path, old_path_len,
new_fd, new_path, new_path_len);
@ -771,6 +858,9 @@ wasi_fd_filestat_get(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(filestat, sizeof(wasi_filestat_t)))
return (wasi_errno_t)-1;
@ -788,6 +878,9 @@ wasi_fd_filestat_set_times(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_filestat_set_times(curfds, fd,
st_atim, st_mtim, fstflags);
}
@ -801,6 +894,9 @@ wasi_fd_filestat_set_size(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_fd_filestat_set_size(curfds, fd, st_size);
}
@ -815,6 +911,9 @@ wasi_path_filestat_get(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr(filestat, sizeof(wasi_filestat_t)))
return (wasi_errno_t)-1;
@ -835,6 +934,9 @@ wasi_path_filestat_set_times(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_path_filestat_set_times(curfds, fd,
flags, path, path_len,
st_atim, st_mtim, fstflags);
@ -850,6 +952,9 @@ wasi_path_symlink(wasm_exec_env_t exec_env,
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_path_symlink(curfds, prestats,
old_path, old_path_len, fd,
new_path, new_path_len);
@ -863,6 +968,9 @@ wasi_path_unlink_file(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_path_unlink_file(curfds, fd, path, path_len);
}
@ -874,6 +982,9 @@ wasi_path_remove_directory(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_path_remove_directory(curfds, fd, path, path_len);
}
@ -888,6 +999,9 @@ wasi_poll_oneoff(wasm_exec_env_t exec_env,
size_t nevents;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
if (!validate_native_addr((void*)in, sizeof(wasi_subscription_t))
|| !validate_native_addr(out, sizeof(wasi_event_t))
|| !validate_native_addr(nevents_app, sizeof(uint32)))
@ -943,6 +1057,9 @@ wasi_sock_recv(wasm_exec_env_t exec_env,
uint32 i;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
total_size = sizeof(iovec_app_t) * (uint64)ri_data_len;
if (!validate_native_addr(ro_datalen_app, (uint32)sizeof(uint32))
|| !validate_native_addr(ro_flags, (uint32)sizeof(wasi_roflags_t))
@ -1000,6 +1117,9 @@ wasi_sock_send(wasm_exec_env_t exec_env,
uint32 i;
wasi_errno_t err;
if (!wasi_ctx)
return (wasi_errno_t)-1;
total_size = sizeof(iovec_app_t) * (uint64)si_data_len;
if (!validate_native_addr(so_datalen_app, sizeof(uint32))
|| total_size >= UINT32_MAX
@ -1046,6 +1166,9 @@ wasi_sock_shutdown(wasm_exec_env_t exec_env,
wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
if (!wasi_ctx)
return (wasi_errno_t)-1;
return wasmtime_ssp_sock_shutdown(curfds, sock, how);
}

View File

@ -0,0 +1,523 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "thread_manager.h"
typedef struct {
bh_list_link l;
void (*destroy_cb)(WASMCluster *);
} DestroyCallBackNode;
static bh_list destroy_callback_list_head;
static bh_list *const destroy_callback_list = &destroy_callback_list_head;
static bh_list cluster_list_head;
static bh_list *const cluster_list = &cluster_list_head;
static korp_mutex cluster_list_lock;
typedef void (*list_visitor)(void *, void *);
static uint32 cluster_max_thread_num = CLUSTER_MAX_THREAD_NUM;
/* Set the maximum thread number, if this function is not called,
the max thread num is defined by CLUSTER_MAX_THREAD_NUM */
void
wasm_cluster_set_max_thread_num(uint32 num)
{
cluster_max_thread_num = num;
}
bool
thread_manager_init()
{
if (bh_list_init(cluster_list) != 0)
return false;
if (os_mutex_init(&cluster_list_lock) != 0)
return false;
return true;
}
void
thread_manager_destroy()
{
WASMCluster *cluster = bh_list_first_elem(cluster_list);
WASMCluster *next;
while (cluster) {
next = bh_list_elem_next(cluster);
wasm_cluster_destroy(cluster);
cluster = next;
}
wasm_cluster_cancel_all_callbacks();
os_mutex_destroy(&cluster_list_lock);
}
static void
traverse_list(bh_list *l, list_visitor visitor, void *user_data)
{
void *next, *node = bh_list_first_elem(l);
while (node) {
next = bh_list_elem_next(node);
visitor(node, user_data);
node = next;
}
}
static bool
allocate_aux_stack(WASMCluster *cluster, uint32 *start, uint32 *size)
{
uint32 i;
/* If the module doesn't have aux stack info,
it can't create any threads */
if (!cluster->stack_segment_occupied)
return false;
os_mutex_lock(&cluster->lock);
for (i = 0; i < cluster_max_thread_num; i++) {
if (!cluster->stack_segment_occupied[i]) {
if (start)
*start = cluster->stack_tops[i];
if (size)
*size = cluster->stack_size;
cluster->stack_segment_occupied[i] = true;
os_mutex_unlock(&cluster->lock);
return true;
}
}
os_mutex_unlock(&cluster->lock);
return false;
}
static bool
free_aux_stack(WASMCluster *cluster, uint32 start)
{
uint32 i;
for (i = 0; i < cluster_max_thread_num; i++) {
if (start == cluster->stack_tops[i]) {
os_mutex_lock(&cluster->lock);
cluster->stack_segment_occupied[i] = false;
os_mutex_unlock(&cluster->lock);
return true;
}
}
return false;
}
WASMCluster *
wasm_cluster_create(WASMExecEnv *exec_env)
{
WASMCluster *cluster;
uint64 total_size;
uint32 aux_stack_start, aux_stack_size, i;
bh_assert(exec_env->cluster == NULL);
if (!(cluster = wasm_runtime_malloc(sizeof(WASMCluster)))) {
LOG_ERROR("thread manager error: failed to allocate memory");
return NULL;
}
memset(cluster, 0, sizeof(WASMCluster));
exec_env->cluster = cluster;
bh_list_init(&cluster->exec_env_list);
bh_list_insert(&cluster->exec_env_list, exec_env);
if (os_mutex_init(&cluster->lock) != 0) {
wasm_runtime_free(cluster);
LOG_ERROR("thread manager error: failed to init mutex");
return NULL;
}
/* Prepare the aux stack top and size for every thread */
if (!wasm_exec_env_get_aux_stack(exec_env,
&aux_stack_start,
&aux_stack_size)) {
LOG_VERBOSE("No aux stack info for this module, can't create thread");
/* If the module don't have aux stack info, don't throw error here,
but remain stack_tops and stack_segment_occupied as NULL */
os_mutex_lock(&cluster_list_lock);
if (bh_list_insert(cluster_list, cluster) != 0) {
os_mutex_unlock(&cluster_list_lock);
goto fail;
}
os_mutex_unlock(&cluster_list_lock);
return cluster;
}
cluster->stack_size = aux_stack_size / (cluster_max_thread_num + 1);
if (cluster->stack_size == 0) {
goto fail;
}
/* Set initial aux stack top to the instance and
aux stack boundary to the main exec_env */
if (!wasm_exec_env_set_aux_stack(exec_env, aux_stack_start,
cluster->stack_size))
goto fail;
if (cluster_max_thread_num != 0) {
total_size = cluster_max_thread_num * sizeof(uint32);
if (total_size >= UINT32_MAX
|| !(cluster->stack_tops =
wasm_runtime_malloc((uint32)total_size))) {
goto fail;
}
memset(cluster->stack_tops, 0, (uint32)total_size);
if (!(cluster->stack_segment_occupied =
wasm_runtime_malloc(cluster_max_thread_num * sizeof(bool)))) {
goto fail;
}
memset(cluster->stack_segment_occupied, 0,
cluster_max_thread_num * sizeof(bool));
/* Reserve space for main instance */
aux_stack_start -= cluster->stack_size;
for (i = 0; i < cluster_max_thread_num; i++) {
cluster->stack_tops[i] = aux_stack_start - cluster->stack_size * i;
}
}
os_mutex_lock(&cluster_list_lock);
if (bh_list_insert(cluster_list, cluster) != 0) {
os_mutex_unlock(&cluster_list_lock);
goto fail;
}
os_mutex_unlock(&cluster_list_lock);
return cluster;
fail:
if (cluster)
wasm_cluster_destroy(cluster);
return NULL;
}
static void
destroy_cluster_visitor(void *node, void *user_data)
{
DestroyCallBackNode *destroy_node = (DestroyCallBackNode *)node;
WASMCluster *cluster = (WASMCluster *)user_data;
destroy_node->destroy_cb(cluster);
}
void
wasm_cluster_destroy(WASMCluster *cluster)
{
traverse_list(destroy_callback_list,
destroy_cluster_visitor, (void *)cluster);
/* Remove the cluster from the cluster list */
os_mutex_lock(&cluster_list_lock);
bh_list_remove(cluster_list, cluster);
os_mutex_unlock(&cluster_list_lock);
os_mutex_destroy(&cluster->lock);
if (cluster->stack_tops)
wasm_runtime_free(cluster->stack_tops);
if (cluster->stack_segment_occupied)
wasm_runtime_free(cluster->stack_segment_occupied);
wasm_runtime_free(cluster);
}
static void
free_node_visitor(void *node, void *user_data)
{
wasm_runtime_free(node);
}
void
wasm_cluster_cancel_all_callbacks()
{
traverse_list(destroy_callback_list, free_node_visitor, NULL);
}
WASMCluster *
wasm_exec_env_get_cluster(WASMExecEnv *exec_env)
{
return exec_env->cluster;
}
bool
wasm_cluster_add_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env)
{
bool ret = true;
exec_env->cluster = cluster;
os_mutex_lock(&cluster->lock);
if (bh_list_insert(&cluster->exec_env_list, exec_env) != 0)
ret = false;
os_mutex_unlock(&cluster->lock);
return ret;
}
bool
wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env)
{
bool ret = true;
bh_assert(exec_env->cluster == cluster);
os_mutex_lock(&cluster->lock);
if (bh_list_remove(&cluster->exec_env_list, exec_env) != 0)
ret = false;
os_mutex_unlock(&cluster->lock);
if (cluster->exec_env_list.len == 0) {
/* exec_env_list empty, destroy the cluster */
wasm_cluster_destroy(cluster);
}
return ret;
}
/* start routine of thread manager */
static void*
thread_manager_start_routine(void *arg)
{
void *ret;
WASMExecEnv *exec_env = (WASMExecEnv *)arg;
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster != NULL);
exec_env->handle = os_self_thread();
ret = exec_env->thread_start_routine(exec_env);
/* Routine exit */
/* Free aux stack space */
free_aux_stack(cluster,
exec_env->aux_stack_boundary + cluster->stack_size);
/* Detach the native thread here to ensure the resources are freed */
wasm_cluster_detach_thread(exec_env);
/* Remove and destroy exec_env */
wasm_cluster_del_exec_env(cluster, exec_env);
wasm_exec_env_destroy_internal(exec_env);
os_thread_exit(ret);
return ret;
}
int32
wasm_cluster_create_thread(WASMExecEnv *exec_env,
wasm_module_inst_t module_inst,
void* (*thread_routine)(void *),
void *arg)
{
WASMCluster *cluster;
WASMExecEnv *new_exec_env;
uint32 aux_stack_start, aux_stack_size;
korp_tid tid;
cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster);
new_exec_env = wasm_exec_env_create_internal(
module_inst, exec_env->wasm_stack_size);
if (!new_exec_env)
return -1;
if (!allocate_aux_stack(cluster, &aux_stack_start, &aux_stack_size)) {
LOG_ERROR("thread manager error: "
"failed to allocate aux stack space for new thread");
goto fail1;
}
/* Set aux stack for current thread */
if (!wasm_exec_env_set_aux_stack(new_exec_env, aux_stack_start,
aux_stack_size)) {
goto fail2;
}
if (!wasm_cluster_add_exec_env(cluster, new_exec_env))
goto fail2;
new_exec_env->thread_start_routine = thread_routine;
new_exec_env->thread_arg = arg;
if (0 != os_thread_create(&tid, thread_manager_start_routine,
(void *)new_exec_env,
APP_THREAD_STACK_SIZE_DEFAULT)) {
goto fail3;
}
return 0;
fail3:
wasm_cluster_del_exec_env(cluster, new_exec_env);
fail2:
/* free the allocated aux stack space */
free_aux_stack(cluster, aux_stack_start);
fail1:
wasm_exec_env_destroy(new_exec_env);
return -1;
}
int32
wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val)
{
return os_thread_join(exec_env->handle, ret_val);
}
int32
wasm_cluster_detach_thread(WASMExecEnv *exec_env)
{
return os_thread_detach(exec_env->handle);
}
void
wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval)
{
WASMCluster *cluster;
cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster);
/* App exit the thread, free the resources before exit native thread */
/* Free aux stack space */
free_aux_stack(cluster,
exec_env->aux_stack_boundary + cluster->stack_size);
/* Detach the native thread here to ensure the resources are freed */
wasm_cluster_detach_thread(exec_env);
/* Remove and destroy exec_env */
wasm_cluster_del_exec_env(cluster, exec_env);
wasm_exec_env_destroy_internal(exec_env);
os_thread_exit(retval);
}
int32
wasm_cluster_cancel_thread(WASMExecEnv *exec_env)
{
/* Set the termination flag */
exec_env->suspend_flags |= 0x01;
return 0;
}
static void
terminate_thread_visitor(void *node, void *user_data)
{
WASMExecEnv *curr_exec_env = (WASMExecEnv *)node;
WASMExecEnv *exec_env = (WASMExecEnv *)user_data;
if (curr_exec_env == exec_env)
return;
wasm_cluster_cancel_thread(curr_exec_env);
wasm_cluster_join_thread(curr_exec_env, NULL);
}
void
wasm_cluster_terminate_all(WASMCluster *cluster)
{
traverse_list(&cluster->exec_env_list,
terminate_thread_visitor, NULL);
}
void
wasm_cluster_terminate_all_except_self(WASMCluster *cluster,
WASMExecEnv *exec_env)
{
traverse_list(&cluster->exec_env_list,
terminate_thread_visitor, (void *)exec_env);
}
bool
wasm_cluster_register_destroy_callback(void (*callback)(WASMCluster *))
{
DestroyCallBackNode *node;
if (!(node = wasm_runtime_malloc(sizeof(DestroyCallBackNode)))) {
LOG_ERROR("thread manager error: failed to allocate memory");
return false;
}
node->destroy_cb = callback;
bh_list_insert(destroy_callback_list, node);
return true;
}
void
wasm_cluster_suspend_thread(WASMExecEnv *exec_env)
{
/* Set the suspend flag */
exec_env->suspend_flags |= 0x02;
}
static void
suspend_thread_visitor(void *node, void *user_data)
{
WASMExecEnv *curr_exec_env = (WASMExecEnv *)node;
WASMExecEnv *exec_env = (WASMExecEnv *)user_data;
if (curr_exec_env == exec_env)
return;
wasm_cluster_suspend_thread(curr_exec_env);
}
void
wasm_cluster_suspend_all(WASMCluster *cluster)
{
traverse_list(&cluster->exec_env_list,
suspend_thread_visitor, NULL);
}
void
wasm_cluster_suspend_all_except_self(WASMCluster *cluster,
WASMExecEnv *exec_env)
{
traverse_list(&cluster->exec_env_list,
suspend_thread_visitor, (void *)exec_env);
}
void
wasm_cluster_resume_thread(WASMExecEnv *exec_env)
{
exec_env->suspend_flags &= ~0x02;
}
static void
resume_thread_visitor(void *node, void *user_data)
{
WASMExecEnv *curr_exec_env = (WASMExecEnv *)node;
wasm_cluster_resume_thread(curr_exec_env);
}
void
wasm_cluster_resume_all(WASMCluster *cluster)
{
traverse_list(&cluster->exec_env_list, resume_thread_visitor, NULL);
}
static void
set_exception_visitor(void *node, void *user_data)
{
WASMExecEnv *curr_exec_env = (WASMExecEnv *)node;
WASMExecEnv *exec_env = (WASMExecEnv *)user_data;
WASMModuleInstanceCommon *module_inst = get_module_inst(exec_env);
WASMModuleInstanceCommon *curr_module_inst =
get_module_inst(curr_exec_env);
const char *exception = wasm_runtime_get_exception(module_inst);
/* skip "Exception: " */
exception += 11;
if (curr_exec_env != exec_env) {
curr_module_inst = get_module_inst(curr_exec_env);
wasm_runtime_set_exception(curr_module_inst, exception);
}
}
void
wasm_cluster_spread_exception(WASMExecEnv *exec_env)
{
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
traverse_list(&cluster->exec_env_list, set_exception_visitor, exec_env);
}

View File

@ -0,0 +1,116 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef _THREAD_MANAGER_H
#define _THREAD_MANAGER_H
#include "bh_common.h"
#include "bh_log.h"
#include "wasm_export.h"
#include "../interpreter/wasm.h"
#include "../common/wasm_runtime_common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct WASMCluster
{
struct WASMCluster *next;
korp_mutex lock;
bh_list exec_env_list;
/* The aux stack of a module with shared memory will be
divided into several segments. This array store the
stack top of different segments */
uint32 *stack_tops;
/* Size of every stack segment */
uint32 stack_size;
/* Record which segments are occupied */
bool *stack_segment_occupied;
} WASMCluster;
void wasm_cluster_set_max_thread_num(uint32 num);
bool
thread_manager_init();
void
thread_manager_destroy();
/* Create cluster */
WASMCluster *
wasm_cluster_create(WASMExecEnv *exec_env);
/* Destroy cluster */
void
wasm_cluster_destroy(WASMCluster *cluster);
/* Get the cluster of the current exec_env */
WASMCluster*
wasm_exec_env_get_cluster(WASMExecEnv *exec_env);
int32
wasm_cluster_create_thread(WASMExecEnv *exec_env,
wasm_module_inst_t module_inst,
void* (*thread_routine)(void *),
void *arg);
int32
wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val);
int32
wasm_cluster_detach_thread(WASMExecEnv *exec_env);
int32
wasm_cluster_cancel_thread(WASMExecEnv *exec_env);
void
wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval);
bool
wasm_cluster_register_destroy_callback(void (*callback)(WASMCluster *));
void
wasm_cluster_cancel_all_callbacks();
void
wasm_cluster_suspend_all(WASMCluster *cluster);
void
wasm_cluster_suspend_all_except_self(WASMCluster *cluster,
WASMExecEnv *exec_env);
void
wasm_cluster_suspend_thread(WASMExecEnv *exec_env);
void
wasm_cluster_resume_thread(WASMExecEnv *exec_env);
void
wasm_cluster_resume_all(WASMCluster *cluster);
void
wasm_cluster_terminate_all(WASMCluster *cluster);
void
wasm_cluster_terminate_all_except_self(WASMCluster *cluster,
WASMExecEnv *exec_env);
bool
wasm_cluster_add_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env);
bool
wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env);
void
wasm_cluster_spread_exception(WASMExecEnv *exec_env);
#ifdef __cplusplus
}
#endif
#endif /* end of _THREAD_MANAGER_H */

View File

@ -0,0 +1,13 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
set (THREAD_MGR_DIR ${CMAKE_CURRENT_LIST_DIR})
add_definitions (-DWASM_ENABLE_THREAD_MGR=1)
include_directories(${THREAD_MGR_DIR})
file (GLOB source_all ${THREAD_MGR_DIR}/*.c)
set (THREAD_MGR_SOURCE ${source_all})