diff --git a/CMakeLists.txt b/CMakeLists.txt index 14a77def5f..eedd9fd2fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,11 @@ if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) set (WAMR_BUILD_LIB_PTHREAD 0) endif () +if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) + # Disable wasi threads by default + set (WAMR_BUILD_LIB_WASI_THREADS 0) +endif() + if (NOT DEFINED WAMR_BUILD_MINI_LOADER) # Disable wasm mini loader by default set (WAMR_BUILD_MINI_LOADER 0) diff --git a/build-scripts/runtime_lib.cmake b/build-scripts/runtime_lib.cmake index c519589517..fb31d3f145 100644 --- a/build-scripts/runtime_lib.cmake +++ b/build-scripts/runtime_lib.cmake @@ -116,6 +116,14 @@ if (WAMR_BUILD_LIB_PTHREAD EQUAL 1) set (WAMR_BUILD_SHARED_MEMORY 1) endif () +if (WAMR_BUILD_LIB_WASI_THREADS EQUAL 1) + include (${IWASM_DIR}/libraries/lib-wasi-threads/lib_wasi_threads.cmake) + # Enable the dependent feature if lib-wasi-threads is enabled + set (WAMR_BUILD_BULK_MEMORY 1) + set (WAMR_BUILD_SHARED_MEMORY 1) +endif () + + if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) set (WAMR_BUILD_THREAD_MGR 1) include (${IWASM_DIR}/libraries/debug-engine/debug_engine.cmake) @@ -179,6 +187,7 @@ set (source_all ${NATIVE_INTERFACE_SOURCE} ${APP_MGR_SOURCE} ${LIB_PTHREAD_SOURCE} + ${LIB_WASI_THREADS_SOURCE} ${THREAD_MGR_SOURCE} ${LIBC_EMCC_SOURCE} ${LIB_RATS_SOURCE} diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index eb19b61960..29d8586191 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -53,6 +53,12 @@ uint32 get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis); #endif +#if WASM_ENABLE_LIB_WASI_THREADS != 0 +uint32 +get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis); +#endif + + uint32 get_libc_emcc_export_apis(NativeSymbol **p_libc_emcc_apis); @@ -412,6 +418,14 @@ wasm_native_init() goto fail; #endif +#if WASM_ENABLE_LIB_WASI_THREADS != 0 + n_native_symbols = get_lib_wasi_threads_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("wasi", native_symbols, + n_native_symbols)) + goto fail; +#endif + #if WASM_ENABLE_LIBC_EMCC != 0 n_native_symbols = get_libc_emcc_export_apis(&native_symbols); if (n_native_symbols > 0 diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake new file mode 100644 index 0000000000..e2765a37db --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIB_WASI_THREADS_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_LIB_WASI_THREADS=1) + +include_directories(${LIB_WASI_THREADS_DIR}) + +file (GLOB source_all ${LIB_WASI_THREADS_DIR}/*.c) + +set (LIB_WASI_THREADS_SOURCE ${source_all}) + diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c new file mode 100644 index 0000000000..d5aa905df4 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#if WASM_ENABLE_INTERP != 0 +#include "wasm_runtime.h" +#endif + +#if WASM_ENABLE_AOT != 0 +#include "aot_runtime.h" +#endif + +const char *THREAD_START_FUNCTION = "wasi_thread_start"; + +typedef struct { + void *start_arg; + wasm_exec_env_t exec_env; + wasm_function_inst_t start_func; +} ThreadInfo; + +static void * +thread_start(void *arg) +{ + ThreadInfo *info = arg; + wasm_exec_env_t exec_env = info->exec_env; + wasm_module_inst_t module_inst = get_module_inst(info->exec_env); + uint32 argv[2]; + + os_thread_signal_init(NULL); + + argv[0] = pthread_self(); + argv[1] = (uint32)(uintptr_t)info->start_arg; + + wasm_runtime_free(arg); + if (!wasm_runtime_call_wasm(exec_env, info->start_func, 2, argv)) { + // TODO action here + } + + wasm_exec_env_destroy(exec_env); + wasm_runtime_deinstantiate_internal(module_inst, true); + + return NULL; +} + +static WASMExecEnv * +wasi_threads_create_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 stack_size = 8192; + + if (!module_inst || !(module = wasm_exec_env_get_module(exec_env))) { + return NULL; + } + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + stack_size = + ((WASMModuleInstance *)module_inst)->default_wasm_stack_size; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + stack_size = + ((AOTModuleInstance *)module_inst)->default_wasm_stack_size; + } +#endif + + if (!(new_module_inst = wasm_runtime_instantiate_internal( + module, true, stack_size, 0, NULL, 0))) { + return NULL; + } + + /* Set custom_data to new module instance */ + 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 + + new_exec_env = + wasm_exec_env_create(new_module_inst, exec_env->wasm_stack_size); + if (!new_exec_env) + goto fail1; + + return new_exec_env; + +fail1: + wasm_exec_env_destroy(new_exec_env); + wasm_runtime_deinstantiate_internal(new_module_inst, true); + + return NULL; +} + +static int +thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 *start_arg) +{ + wasm_module_inst_t new_module_inst; + wasm_exec_env_t new_exec_env; + wasm_function_inst_t start_func; + ThreadInfo *info = NULL; + pthread_t th; + int ret = 0; + + new_exec_env = wasi_threads_create_exec_env(exec_env); + if (!new_exec_env) + return -1; + + new_module_inst = get_module_inst(new_exec_env); + + start_func = wasm_runtime_lookup_function(new_module_inst, THREAD_START_FUNCTION, NULL); + if (!start_func) { + ret = -1; + goto fail; + } + + info = wasm_runtime_malloc(sizeof(ThreadInfo)); + if (!info) + goto fail; + + + info->start_arg = start_arg; + info->start_func = start_func; + info->exec_env = new_exec_env; + + ret = pthread_create(&th, NULL, thread_start, info); + if (ret != 0) + goto fail; + + return ret; + +fail: + if (info) + wasm_runtime_free(info); + + wasm_exec_env_destroy(new_exec_env); + + return ret; +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_lib_wasi_threads[] = { REG_NATIVE_FUNC( + thread_spawn, "(*)i") }; + +uint32 +get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis) +{ + *p_lib_wasi_threads_apis = native_symbols_lib_wasi_threads; + return sizeof(native_symbols_lib_wasi_threads) / sizeof(NativeSymbol); +} diff --git a/product-mini/platforms/linux/CMakeLists.txt b/product-mini/platforms/linux/CMakeLists.txt index d3285be19e..46d0007a10 100644 --- a/product-mini/platforms/linux/CMakeLists.txt +++ b/product-mini/platforms/linux/CMakeLists.txt @@ -85,6 +85,11 @@ if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) set (WAMR_BUILD_LIB_PTHREAD 0) endif () +if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) + # Disable wasi threads by default + set (WAMR_BUILD_LIB_WASI_THREADS 0) +endif() + if (NOT DEFINED WAMR_BUILD_MINI_LOADER) # Disable wasm mini loader by default set (WAMR_BUILD_MINI_LOADER 0) diff --git a/samples/wasi-threads/CMakeLists.txt b/samples/wasi-threads/CMakeLists.txt new file mode 100644 index 0000000000..71d6fe8b32 --- /dev/null +++ b/samples/wasi-threads/CMakeLists.txt @@ -0,0 +1,81 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +include(CheckPIESupported) + +project(native_threads_sample) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault 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 Release) +endif () + +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_LIB_WASI_THREADS 1) +set(WAMR_BUILD_LIBC_WASI 1) + +# compiling and linking 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") + +# 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}) +################################################ + + +################ wasm application ################ +add_subdirectory(wasm-apps) + +################ wamr runtime ################ +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set (RUNTIME_SOURCE_ALL + ${CMAKE_CURRENT_LIST_DIR}/../../product-mini/platforms/linux/main.c + ${UNCOMMON_SHARED_SOURCE} +) +add_executable (iwasm ${RUNTIME_SOURCE_ALL}) +check_pie_supported() +set_target_properties (iwasm PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_link_libraries(iwasm vmlib -lpthread -lm -ldl) + diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000000..1a466d732c --- /dev/null +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(wasm-apps) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-pthread -Qunused-arguments") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -z stack-size=32768 -mbulk-memory") +set (CMAKE_C_COMPILER_TARGET "wasm32-wasi") +set (CMAKE_C_COMPILER "clang") + +set (DEFINED_SYMBOLS +"${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt") + +set (CMAKE_EXE_LINKER_FLAGS + "-Wl,--shared-memory,--max-memory=1048576, \ + -Wl,--no-entry, \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors \ + -Wl,--export=main -Wl,--export=__main_argc_argv \ + -Wl,--export=wasi_thread_start \ + -Wl,--allow-undefined" + #-Wl,--allow-undefined-file=${DEFINED_SYMBOLS}" +) + +add_executable(test.wasm main.c) +target_link_libraries(test.wasm) diff --git a/samples/wasi-threads/wasm-apps/main.c b/samples/wasi-threads/wasm-apps/main.c new file mode 100644 index 0000000000..61d2b61616 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/main.c @@ -0,0 +1,64 @@ +#include +#include +#include + +#define SAFE_CALL(call) \ + do { \ + int ret = (call); \ + if (ret != 0) { \ + printf("Failed to call %s: %d\n", #call, ret); \ + return -1; \ + } \ + } while (0) + +pthread_cond_t cond; +pthread_mutex_t mp; + +int global_value = 0; + +void +sleep(int64_t time_ns) +{ + int dummy = 0; + __builtin_wasm_memory_atomic_wait32(&dummy, dummy, time_ns); +} + +__attribute__((export_name("wasi_thread_start"))) int +wasi_thread_start(int thread_id, int *start_arg) +{ + printf("in a new thread; thread_id: %d\n", thread_id); + printf("sleeping\n"); + sleep(4ll * 1000 * 1000 * 1000); + printf("done with sleeping!\n"); + pthread_mutex_lock(&mp); + global_value = 12; + + int ret = pthread_cond_signal(&cond); + pthread_mutex_unlock(&mp); + return 0; +} + +int +main() +{ + struct timespec timeout = { 0 }; + SAFE_CALL(clock_gettime(CLOCK_REALTIME, &timeout)); + + timeout.tv_sec += 10; + + SAFE_CALL(pthread_cond_init(&cond, NULL)); + + SAFE_CALL(__wasi_thread_spawn(NULL)); + + SAFE_CALL(pthread_mutex_lock(&mp)); + while (global_value != 12) { + SAFE_CALL(pthread_cond_timedwait(&cond, &mp, &timeout)); + } + + global_value += 4; + SAFE_CALL(pthread_mutex_unlock(&mp)); + + printf("new value %d\n", global_value); + + return 0; +} \ No newline at end of file