Просмотр исходного кода

Merge branch 'main' into dev/aot_stack_frame

Wenyong Huang 2 лет назад
Родитель
Сommit
7158e3957a
31 измененных файлов с 711 добавлено и 131 удалено
  1. 10 2
      .github/workflows/compilation_on_android_ubuntu.yml
  2. 5 0
      .github/workflows/nightly_run.yml
  3. 6 22
      core/iwasm/common/wasm_runtime_common.c
  4. 10 0
      core/iwasm/interpreter/wasm_runtime.h
  5. 1 1
      core/iwasm/libraries/lib-rats/lib_rats.cmake
  6. 65 0
      core/iwasm/libraries/lib-wasi-threads/stress-test/build.sh
  7. 27 0
      core/iwasm/libraries/lib-wasi-threads/stress-test/errorcheck_mutex_stress_test.c
  8. 3 0
      core/iwasm/libraries/lib-wasi-threads/stress-test/manifest.json
  9. 229 0
      core/iwasm/libraries/lib-wasi-threads/stress-test/mutex_common.h
  10. 20 0
      core/iwasm/libraries/lib-wasi-threads/stress-test/normal_mutex_stress_test.c
  11. 65 0
      core/iwasm/libraries/lib-wasi-threads/stress-test/recursive_mutex_stress_test.c
  12. 1 1
      core/iwasm/libraries/lib-wasi-threads/stress-test/spawn_stress_test.c
  13. 4 2
      core/iwasm/libraries/lib-wasi-threads/stress-test/stress_test_threads_creation.c
  14. 4 1
      core/iwasm/libraries/lib-wasi-threads/test/build.sh
  15. 1 1
      core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c
  16. 1 1
      core/iwasm/libraries/lib-wasi-threads/test/global_atomic.c
  17. 1 1
      core/iwasm/libraries/lib-wasi-threads/test/global_lock.c
  18. 0 6
      core/iwasm/libraries/lib-wasi-threads/test/skip.json
  19. 1 1
      core/iwasm/libraries/lib-wasi-threads/test/trap_after_main_thread_finishes.c
  20. 1 1
      core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c
  21. 10 0
      core/iwasm/libraries/lib-wasi-threads/tid_allocator.h
  22. 6 0
      core/iwasm/libraries/lib-wasi-threads/unit-test/lib_wasi_threads_unit_tests.cmake
  23. 62 0
      core/iwasm/libraries/lib-wasi-threads/unit-test/test_tid_allocator.cpp
  24. 60 55
      core/iwasm/libraries/thread-mgr/thread_manager.c
  25. 1 1
      core/iwasm/libraries/thread-mgr/thread_manager.h
  26. 20 0
      core/shared/platform/common/posix/posix_sleep.c
  27. 2 8
      samples/wasi-threads/wasm-apps/no_pthread.c
  28. 4 0
      samples/wasi-threads/wasm-apps/wasi_thread_start.h
  29. 51 0
      tests/unit/CMakeLists.txt
  30. 21 0
      tests/unit/main.cpp
  31. 19 27
      tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh

+ 10 - 2
.github/workflows/compilation_on_android_ubuntu.yml

@@ -72,7 +72,7 @@ jobs:
     with:
     with:
       os: "ubuntu-22.04"
       os: "ubuntu-22.04"
       arch: "X86"
       arch: "X86"
-  
+
   build_wamrc:
   build_wamrc:
     needs:
     needs:
       [build_llvm_libraries_on_ubuntu_2204]
       [build_llvm_libraries_on_ubuntu_2204]
@@ -241,6 +241,14 @@ jobs:
           cmake --build . --config Release --parallel 4
           cmake --build . --config Release --parallel 4
         working-directory: product-mini/platforms/${{ matrix.platform }}
         working-directory: product-mini/platforms/${{ matrix.platform }}
 
 
+      - name: Build and run unit tests
+        run: |
+          mkdir build-unittests && cd build-unittests
+          cmake .. ${{ matrix.make_options_run_mode }} ${{ matrix.make_options_feature }}
+          cmake --build . --config Release --parallel 4
+          ctest
+        working-directory: tests/unit
+
   build_samples_wasm_c_api:
   build_samples_wasm_c_api:
     needs:
     needs:
       [
       [
@@ -483,7 +491,7 @@ jobs:
           sudo tar -xzf wasi-sdk-*.tar.gz
           sudo tar -xzf wasi-sdk-*.tar.gz
           sudo mv wasi-sdk-20.0 wasi-sdk
           sudo mv wasi-sdk-20.0 wasi-sdk
 
 
-      # It is a temporary solution until new wasi-sdk that includes bug fixes is released 
+      # It is a temporary solution until new wasi-sdk that includes bug fixes is released
       - name: build wasi-libc from source
       - name: build wasi-libc from source
         if: matrix.test_option == '$WASI_TEST_OPTIONS'
         if: matrix.test_option == '$WASI_TEST_OPTIONS'
         run: |
         run: |

+ 5 - 0
.github/workflows/nightly_run.yml

@@ -599,6 +599,11 @@ jobs:
         run: bash build.sh --sysroot "$SYSROOT_PATH"
         run: bash build.sh --sysroot "$SYSROOT_PATH"
         working-directory: ./core/iwasm/libraries/lib-wasi-threads/test/
         working-directory: ./core/iwasm/libraries/lib-wasi-threads/test/
 
 
+      - name: Build WASI thread stress tests
+        if: matrix.test_option == '$WASI_TEST_OPTIONS'
+        run: bash build.sh --sysroot "$SYSROOT_PATH"
+        working-directory: ./core/iwasm/libraries/lib-wasi-threads/stress-test/
+
       - name: build socket api tests
       - name: build socket api tests
         if: matrix.test_option == '$WASI_TEST_OPTIONS'
         if: matrix.test_option == '$WASI_TEST_OPTIONS'
         run: bash build.sh
         run: bash build.sh

+ 6 - 22
core/iwasm/common/wasm_runtime_common.c

@@ -2377,12 +2377,7 @@ wasm_runtime_get_exec_env_singleton(WASMModuleInstanceCommon *module_inst_comm)
 void
 void
 wasm_set_exception(WASMModuleInstance *module_inst, const char *exception)
 wasm_set_exception(WASMModuleInstance *module_inst, const char *exception)
 {
 {
-    WASMExecEnv *exec_env = NULL;
-
-#if WASM_ENABLE_SHARED_MEMORY != 0
-    if (module_inst->memory_count > 0)
-        shared_memory_lock(module_inst->memories[0]);
-#endif
+    exception_lock(module_inst);
     if (exception) {
     if (exception) {
         snprintf(module_inst->cur_exception, sizeof(module_inst->cur_exception),
         snprintf(module_inst->cur_exception, sizeof(module_inst->cur_exception),
                  "Exception: %s", exception);
                  "Exception: %s", exception);
@@ -2390,19 +2385,14 @@ wasm_set_exception(WASMModuleInstance *module_inst, const char *exception)
     else {
     else {
         module_inst->cur_exception[0] = '\0';
         module_inst->cur_exception[0] = '\0';
     }
     }
-#if WASM_ENABLE_SHARED_MEMORY != 0
-    if (module_inst->memory_count > 0)
-        shared_memory_unlock(module_inst->memories[0]);
-#endif
+    exception_unlock(module_inst);
 
 
 #if WASM_ENABLE_THREAD_MGR != 0
 #if WASM_ENABLE_THREAD_MGR != 0
-    exec_env =
+    WASMExecEnv *exec_env =
         wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst);
         wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst);
     if (exec_env) {
     if (exec_env) {
-        wasm_cluster_spread_exception(exec_env, exception ? false : true);
+        wasm_cluster_spread_exception(exec_env, exception);
     }
     }
-#else
-    (void)exec_env;
 #endif
 #endif
 }
 }
 
 
@@ -2453,10 +2443,7 @@ wasm_copy_exception(WASMModuleInstance *module_inst, char *exception_buf)
 {
 {
     bool has_exception = false;
     bool has_exception = false;
 
 
-#if WASM_ENABLE_SHARED_MEMORY != 0
-    if (module_inst->memory_count > 0)
-        shared_memory_lock(module_inst->memories[0]);
-#endif
+    exception_lock(module_inst);
     if (module_inst->cur_exception[0] != '\0') {
     if (module_inst->cur_exception[0] != '\0') {
         /* NULL is passed if the caller is not interested in getting the
         /* NULL is passed if the caller is not interested in getting the
          * exception content, but only in knowing if an exception has been
          * exception content, but only in knowing if an exception has been
@@ -2468,10 +2455,7 @@ wasm_copy_exception(WASMModuleInstance *module_inst, char *exception_buf)
                         sizeof(module_inst->cur_exception));
                         sizeof(module_inst->cur_exception));
         has_exception = true;
         has_exception = true;
     }
     }
-#if WASM_ENABLE_SHARED_MEMORY != 0
-    if (module_inst->memory_count > 0)
-        shared_memory_unlock(module_inst->memories[0]);
-#endif
+    exception_unlock(module_inst);
 
 
     return has_exception;
     return has_exception;
 }
 }

+ 10 - 0
core/iwasm/interpreter/wasm_runtime.h

@@ -668,6 +668,16 @@ void
 wasm_propagate_wasi_args(WASMModule *module);
 wasm_propagate_wasi_args(WASMModule *module);
 #endif
 #endif
 
 
+#if WASM_ENABLE_THREAD_MGR != 0
+void
+exception_lock(WASMModuleInstance *module_inst);
+void
+exception_unlock(WASMModuleInstance *module_inst);
+#else
+#define exception_lock(module_inst) (void)(module_inst)
+#define exception_unlock(module_inst) (void)(module_inst)
+#endif
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 1 - 1
core/iwasm/libraries/lib-rats/lib_rats.cmake

@@ -23,7 +23,7 @@ include(FetchContent)
 set(RATS_BUILD_MODE "sgx"
 set(RATS_BUILD_MODE "sgx"
     CACHE INTERNAL "Select build mode for librats(host|occlum|sgx|wasm)")
     CACHE INTERNAL "Select build mode for librats(host|occlum|sgx|wasm)")
 set(RATS_INSTALL_PATH  "${CMAKE_BINARY_DIR}/librats" CACHE INTERNAL "")
 set(RATS_INSTALL_PATH  "${CMAKE_BINARY_DIR}/librats" CACHE INTERNAL "")
-set(BUILD_SAMPLES OFF)
+set(BUILD_SAMPLES OFF CACHE BOOL "Disable de compilation of the librats samples" FORCE)
 
 
 FetchContent_Declare(
 FetchContent_Declare(
     librats
     librats

+ 65 - 0
core/iwasm/libraries/lib-wasi-threads/stress-test/build.sh

@@ -0,0 +1,65 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+
+set -eo pipefail
+CC=${CC:=/opt/wasi-sdk/bin/clang}
+WAMR_DIR=../../../../..
+
+show_usage() {
+    echo "Usage: $0 [--sysroot PATH_TO_SYSROOT]"
+    echo "--sysroot PATH_TO_SYSROOT specify to build with custom sysroot for wasi-libc"
+}
+
+while [[ $# -gt 0 ]]; do
+    key="$1"
+    case $key in
+        --sysroot)
+            sysroot_path="$2"
+            shift
+            shift
+            ;;
+        --help)
+            show_usage
+            exit
+            ;;
+        *)
+            echo "Unknown option: $1"
+            exit 1
+            ;;
+    esac
+done
+
+rm -rf *.wasm
+rm -rf *.aot
+
+for test_c in *.c; do
+    test_wasm="$(basename $test_c .c).wasm"
+
+    if [[ -n "$sysroot_path" ]]; then 
+        if [ ! -d "$sysroot_path" ]; then 
+            echo "Directory $sysroot_path  doesn't exist. Aborting"
+            exit 1
+        fi
+        sysroot_command="--sysroot $sysroot_path"
+    fi
+    
+    echo "Compiling $test_c to $test_wasm"
+    $CC \
+        -target wasm32-wasi-threads \
+        -O2 \
+        -Wall \
+        -pthread \
+        -z stack-size=32768 \
+        -Wl,--export=__heap_base \
+        -Wl,--export=__data_end \
+        -Wl,--shared-memory,--max-memory=1966080 \
+        -Wl,--export=wasi_thread_start \
+        -Wl,--export=malloc \
+        -Wl,--export=free \
+        $sysroot_command \
+        $test_c -o $test_wasm
+done

+ 27 - 0
core/iwasm/libraries/lib-wasi-threads/stress-test/errorcheck_mutex_stress_test.c

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <pthread.h>
+#include <errno.h>
+#include "mutex_common.h"
+
+int
+main()
+{
+    pthread_mutex_t mutex;
+
+    // Set mutex type to errorcheck. This type provides some additional checks
+    // (for example returns EDEADLK instead of deadlocking in some cases)
+    pthread_mutexattr_t mutex_attr;
+    pthread_mutexattr_init(&mutex_attr);
+    pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK);
+
+    pthread_mutex_init(&mutex, &mutex_attr);
+    pthread_mutexattr_destroy(&mutex_attr);
+
+    run_common_tests(&mutex);
+    fprintf(stderr, "Errorcheck mutex test is completed\n");
+    pthread_mutex_destroy(&mutex);
+}

+ 3 - 0
core/iwasm/libraries/lib-wasi-threads/stress-test/manifest.json

@@ -0,0 +1,3 @@
+{
+    "name": "lib-wasi-threads stress tests"
+}

+ 229 - 0
core/iwasm/libraries/lib-wasi-threads/stress-test/mutex_common.h

@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef MUTEX_COMMON_H
+#define MUTEX_COMMON_H
+
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+
+enum Constants {
+    NUM_ITER = 250000,
+    NUM_THREADS = 12,
+    NUM_RETRY = 8,
+    RETRY_SLEEP_TIME_US = 1000,
+};
+
+// We're counting how many times each thread was called using this array
+// Main thread is also counted here so we need to make arrays bigger
+typedef struct {
+    int tids[NUM_THREADS + 1];
+    int calls[NUM_THREADS + 1];
+} StatCollector;
+
+typedef struct {
+    pthread_mutex_t *mutex;
+    StatCollector stat;
+    int counter;
+    bool is_sleeping;
+} MutexCounter;
+
+// This enum defines whether thread should sleep to increase contention
+enum SleepState {
+    NON_SLEEP = 0,
+    SLEEP = 1,
+};
+
+void
+mutex_counter_init(MutexCounter *mutex_counter, pthread_mutex_t *mutex,
+                   enum SleepState is_sleeping)
+{
+    memset(mutex_counter, 0, sizeof(*mutex_counter));
+    mutex_counter->mutex = mutex;
+    mutex_counter->is_sleeping = is_sleeping;
+}
+
+// This function spawns the thread using exponential retries if it receives
+// EAGAIN
+static inline void
+spawn_thread(pthread_t *tid, void *func, void *arg)
+{
+    int status_code = -1;
+    int timeout_us = RETRY_SLEEP_TIME_US;
+    for (int tries = 0; status_code != 0 && tries < NUM_RETRY; ++tries) {
+        status_code = pthread_create(tid, NULL, (void *(*)(void *))func, arg);
+        assert(status_code == 0 || status_code == EAGAIN);
+        if (status_code == EAGAIN) {
+            usleep(timeout_us);
+            timeout_us *= 2;
+        }
+    }
+
+    assert(status_code == 0 && "Thread creation should succeed");
+}
+
+// This function adds tid to our stat
+static inline void
+add_to_stat(StatCollector *stat, int tid)
+{
+    int tid_num = 0;
+    for (; tid_num < NUM_THREADS + 1 && stat->tids[tid_num] != 0; ++tid_num) {
+        if (stat->tids[tid_num] == tid) {
+            stat->calls[tid_num]++;
+            return;
+        }
+    }
+
+    assert(tid_num < NUM_THREADS + 1);
+    stat->tids[tid_num] = tid;
+    stat->calls[tid_num] = 1;
+}
+
+// This function prints number of calls by TID
+static inline void
+print_stat(StatCollector *stat)
+{
+    fprintf(stderr, "Thread calls count by TID\n");
+    for (int i = 0; i < NUM_THREADS + 1; ++i) {
+        if (stat->tids[i] != 0) {
+            fprintf(stderr, "TID: %d; Calls: %d\n", stat->tids[i],
+                    stat->calls[i]);
+        }
+    }
+}
+
+// This function is run by the threads, it increases counter in a loop and then
+// sleeps after unlocking the mutex to provide better contention
+static inline void *
+inc_shared_variable(void *arg)
+{
+    MutexCounter *mutex_counter = (MutexCounter *)(arg);
+    int sleep_us = 0;
+    while (!pthread_mutex_lock(mutex_counter->mutex)
+           && mutex_counter->counter < NUM_ITER) {
+        mutex_counter->counter++;
+        add_to_stat(&mutex_counter->stat, (int)(pthread_self()));
+        if (mutex_counter->is_sleeping) {
+            sleep_us = rand() % 1000;
+        }
+
+        assert(pthread_mutex_unlock(mutex_counter->mutex) == 0
+               && "Should be able to unlock a mutex");
+        if (mutex_counter->is_sleeping) {
+            usleep(sleep_us);
+        }
+    }
+
+    assert(mutex_counter->counter == NUM_ITER);
+    assert(pthread_mutex_unlock(mutex_counter->mutex) == 0
+           && "Should be able to unlock the mutex after test execution");
+
+    return NULL;
+}
+
+// Locking and unlocking a mutex in a single thread.
+static inline void *
+same_thread_lock_unlock_test(void *mutex)
+{
+    for (int i = 0; i < NUM_ITER; ++i) {
+        assert(pthread_mutex_lock(mutex) == 0
+               && "Main thread should be able to lock a mutex");
+        assert(pthread_mutex_unlock(mutex) == 0
+               && "Main thread should be able to unlock a mutex");
+    }
+
+    return NULL;
+}
+
+// This function spawns a thread that locks and unlocks a mutex `NUM_ITER` times
+// in a row
+static inline void
+same_non_main_thread_lock_unlock_test(pthread_mutex_t *mutex)
+{
+    pthread_t tid = 0;
+    spawn_thread(&tid, same_thread_lock_unlock_test, mutex);
+
+    assert(tid != 0 && "TID can't be 0 after successful thread creation");
+    assert(pthread_join(tid, NULL) == 0
+           && "Thread should be joined successfully");
+}
+
+// This function checks basic contention between main and non-main thread
+// increasing the shared variable
+static inline void
+two_threads_inc_test(pthread_mutex_t *mutex)
+{
+    MutexCounter mutex_counter;
+    mutex_counter_init(&mutex_counter, mutex, false);
+
+    pthread_t tid = 0;
+    spawn_thread(&tid, inc_shared_variable, &mutex_counter);
+
+    assert(tid != 0 && "TID can't be 0 after successful thread creation");
+    inc_shared_variable(&mutex_counter);
+    assert(pthread_join(tid, NULL) == 0
+           && "Thread should be joined without errors");
+    assert(mutex_counter.counter == NUM_ITER);
+}
+
+// This function creates number of threads specified by NUM_THREADS and run
+// concurrent increasing of shared variable
+static inline void
+max_threads_inc_test(pthread_mutex_t *mutex, int threads_num,
+                     enum SleepState is_sleeping)
+{
+    MutexCounter mutex_counter;
+    mutex_counter_init(&mutex_counter, mutex, is_sleeping);
+
+    pthread_t tids[threads_num];
+    for (int i = 0; i < threads_num; ++i) {
+        spawn_thread(&tids[i], inc_shared_variable, &mutex_counter);
+    }
+
+    inc_shared_variable(&mutex_counter);
+
+    for (int i = 0; i < threads_num; ++i) {
+        assert(pthread_join(tids[i], NULL) == 0
+               && "Thread should be joined without errors");
+    }
+
+    print_stat(&mutex_counter.stat);
+}
+
+// This function just runs all the tests described above
+static inline void
+run_common_tests(pthread_mutex_t *mutex)
+{
+    srand(time(NULL));
+
+    fprintf(stderr, "Starting same_thread_lock_unlock_test test\n");
+    same_thread_lock_unlock_test(mutex);
+    fprintf(stderr, "Finished same_thread_lock_unlock_test test\n");
+
+    fprintf(stderr, "Starting same_non_main_thread_lock_unlock_test test\n");
+    same_non_main_thread_lock_unlock_test(mutex);
+    fprintf(stderr, "Finished same_non_main_thread_lock_unlock_test test\n");
+
+    fprintf(stderr, "Starting two_threads_inc_test test\n");
+    two_threads_inc_test(mutex);
+    fprintf(stderr, "Finished two_threads_inc_test test\n");
+
+    fprintf(stderr, "Starting max_threads_inc_test_sleep test\n");
+    max_threads_inc_test(mutex, NUM_THREADS, SLEEP);
+    fprintf(stderr, "Finished concurrent_inc sleep test\n");
+
+    fprintf(stderr, "Starting max_threads_inc_test_non_sleep test\n");
+    max_threads_inc_test(mutex, NUM_THREADS, NON_SLEEP);
+    fprintf(stderr, "Finished max_threads_inc_test test\n");
+}
+
+#endif // MUTEX_COMMON_H

+ 20 - 0
core/iwasm/libraries/lib-wasi-threads/stress-test/normal_mutex_stress_test.c

@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <pthread.h>
+#include <errno.h>
+#include "mutex_common.h"
+
+int
+main()
+{
+    pthread_mutex_t mutex;
+    pthread_mutex_init(&mutex, NULL);
+
+    run_common_tests(&mutex);
+
+    fprintf(stderr, "Normal mutex test is completed\n");
+    pthread_mutex_destroy(&mutex);
+}

+ 65 - 0
core/iwasm/libraries/lib-wasi-threads/stress-test/recursive_mutex_stress_test.c

@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <pthread.h>
+#include <errno.h>
+#include "mutex_common.h"
+
+void
+multiple_same_thread_lock(void *mutex)
+{
+    for (int i = 0; i < 100; ++i) {
+        assert(pthread_mutex_lock(mutex) == 0
+               && "Recursive mutex should allow multiple locking");
+    }
+
+    for (int i = 0; i < 100; ++i) {
+        assert(pthread_mutex_unlock(mutex) == 0
+               && "Recursive mutex should allow multiple unlocking");
+    }
+}
+
+void *
+same_thread_multiple_rec_mutex_lock(void *mutex)
+{
+    for (int i = 0; i < NUM_ITER; ++i) {
+        multiple_same_thread_lock(mutex);
+    }
+
+    return NULL;
+}
+
+int
+main()
+{
+    pthread_mutex_t mutex;
+
+    // Set mutex type to recursive. This type allows multiple locking and
+    // unlocking within the same thread
+    pthread_mutexattr_t mutex_attr;
+    pthread_mutexattr_init(&mutex_attr);
+    pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
+
+    pthread_mutex_init(&mutex, &mutex_attr);
+    pthread_mutexattr_destroy(&mutex_attr);
+
+    run_common_tests(&mutex);
+
+    fprintf(stderr, "Starting same_thread_multiple_rec_mutex_lock test\n");
+    same_thread_multiple_rec_mutex_lock(&mutex);
+    fprintf(stderr, "Finished same_thread_multiple_rec_mutex_lock test\n");
+
+    fprintf(stderr, "Starting same_thread_multiple_rec_mutex_lock test in "
+                    "non-main thread\n");
+    pthread_t tid;
+    spawn_thread(&tid, same_thread_multiple_rec_mutex_lock, &mutex);
+    assert(pthread_join(tid, NULL) == 0
+           && "Non-main thread should be joined successfully");
+    fprintf(stderr, "Finished same_thread_multiple_rec_mutex_lock test in "
+                    "non-main thread\n");
+
+    fprintf(stderr, "Recursive mutex test is completed\n");
+    pthread_mutex_destroy(&mutex);
+}

+ 1 - 1
core/iwasm/libraries/lib-wasi-threads/test/spawn_stress_test.c → core/iwasm/libraries/lib-wasi-threads/stress-test/spawn_stress_test.c

@@ -19,7 +19,7 @@
 enum CONSTANTS {
 enum CONSTANTS {
     NUM_ITER = 100000,
     NUM_ITER = 100000,
     NUM_RETRY = 8,
     NUM_RETRY = 8,
-    MAX_NUM_THREADS = 8,
+    MAX_NUM_THREADS = 12,
     RETRY_SLEEP_TIME_US = 2000,
     RETRY_SLEEP_TIME_US = 2000,
 };
 };
 
 

+ 4 - 2
core/iwasm/libraries/lib-wasi-threads/test/stress_test_threads_creation.c → core/iwasm/libraries/lib-wasi-threads/stress-test/stress_test_threads_creation.c

@@ -12,7 +12,7 @@
 enum CONSTANTS {
 enum CONSTANTS {
     NUM_ITER = 200000,
     NUM_ITER = 200000,
     NUM_RETRY = 8,
     NUM_RETRY = 8,
-    MAX_NUM_THREADS = 8,
+    MAX_NUM_THREADS = 12,
     RETRY_SLEEP_TIME_US = 4000,
     RETRY_SLEEP_TIME_US = 4000,
     SECOND = 1000 * 1000 * 1000
     SECOND = 1000 * 1000 * 1000
 };
 };
@@ -72,7 +72,9 @@ main(int argc, char **argv)
     }
     }
 
 
     while ((__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) != 0)) {
     while ((__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) != 0)) {
-        __builtin_wasm_memory_atomic_wait32(&threads_in_use, 0, SECOND);
+        // Casting to int* to supress compiler warning
+        __builtin_wasm_memory_atomic_wait32((int *)(&threads_in_use), 0,
+                                            SECOND);
     }
     }
 
 
     assert(__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) == 0);
     assert(__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) == 0);

+ 4 - 1
core/iwasm/libraries/lib-wasi-threads/test/build.sh

@@ -34,7 +34,10 @@ while [[ $# -gt 0 ]]; do
 done
 done
 
 
 # Stress tests names
 # Stress tests names
-thread_start_file_exclusions=("spawn_stress_test.wasm" "linear_memory_size_update.wasm" "stress_test_threads_creation.wasm")
+thread_start_file_exclusions=("linear_memory_size_update.wasm")
+
+rm -rf *.wasm
+rm -rf *.aot
 
 
 for test_c in *.c; do
 for test_c in *.c; do
     test_wasm="$(basename $test_c .c).wasm"
     test_wasm="$(basename $test_c .c).wasm"

+ 1 - 1
core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c

@@ -65,7 +65,7 @@ main(int argc, char **argv)
         assert(start_args_init(&data[i].base));
         assert(start_args_init(&data[i].base));
         thread_ids[i] = __wasi_thread_spawn(&data[i]);
         thread_ids[i] = __wasi_thread_spawn(&data[i]);
         printf("Thread created with id=%d\n", thread_ids[i]);
         printf("Thread created with id=%d\n", thread_ids[i]);
-        assert(thread_ids[i] > 0 && "Thread creation failed");
+        ASSERT_VALID_TID(thread_ids[i]);
 
 
         for (int j = 0; j < i; j++) {
         for (int j = 0; j < i; j++) {
             assert(thread_ids[i] != thread_ids[j] && "Duplicated TIDs");
             assert(thread_ids[i] != thread_ids[j] && "Duplicated TIDs");

+ 1 - 1
core/iwasm/libraries/lib-wasi-threads/test/global_atomic.c

@@ -49,7 +49,7 @@ main(int argc, char **argv)
     for (int i = 0; i < NUM_THREADS; i++) {
     for (int i = 0; i < NUM_THREADS; i++) {
         assert(start_args_init(&data[i].base));
         assert(start_args_init(&data[i].base));
         thread_ids[i] = __wasi_thread_spawn(&data[i]);
         thread_ids[i] = __wasi_thread_spawn(&data[i]);
-        assert(thread_ids[i] > 0 && "Thread creation failed");
+        ASSERT_VALID_TID(thread_ids[i]);
     }
     }
 
 
     printf("Wait for threads to finish\n");
     printf("Wait for threads to finish\n");

+ 1 - 1
core/iwasm/libraries/lib-wasi-threads/test/global_lock.c

@@ -61,7 +61,7 @@ main(int argc, char **argv)
     for (int i = 0; i < NUM_THREADS; i++) {
     for (int i = 0; i < NUM_THREADS; i++) {
         assert(start_args_init(&data[i].base));
         assert(start_args_init(&data[i].base));
         thread_ids[i] = __wasi_thread_spawn(&data[i]);
         thread_ids[i] = __wasi_thread_spawn(&data[i]);
-        assert(thread_ids[i] > 0 && "Thread creation failed");
+        ASSERT_VALID_TID(thread_ids[i]);
     }
     }
 
 
     printf("Wait for threads to finish\n");
     printf("Wait for threads to finish\n");

+ 0 - 6
core/iwasm/libraries/lib-wasi-threads/test/skip.json

@@ -1,6 +0,0 @@
-{
-    "lib-wasi-threads tests": {
-        "spawn_stress_test": "Stress tests are incompatible with the other part and executed differently",
-        "stress_test_threads_creation": "Stress tests are incompatible with the other part and executed differently"
-    }
-}

+ 1 - 1
core/iwasm/libraries/lib-wasi-threads/test/trap_after_main_thread_finishes.c

@@ -38,7 +38,7 @@ main(int argc, char **argv)
 
 
     assert(start_args_init(&data.base));
     assert(start_args_init(&data.base));
     int thread_id = __wasi_thread_spawn(&data);
     int thread_id = __wasi_thread_spawn(&data);
-    assert(thread_id > 0 && "Thread creation failed");
+    ASSERT_VALID_TID(thread_id);
 
 
     return EXIT_SUCCESS;
     return EXIT_SUCCESS;
 }
 }

+ 1 - 1
core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c

@@ -69,7 +69,7 @@ main(int argc, char **argv)
         data[i].iteration = i;
         data[i].iteration = i;
 
 
         thread_ids[i] = __wasi_thread_spawn(&data[i]);
         thread_ids[i] = __wasi_thread_spawn(&data[i]);
-        assert(thread_ids[i] > 0 && "Thread creation failed");
+        ASSERT_VALID_TID(thread_ids[i]);
     }
     }
 
 
     printf("Wait for threads to finish\n");
     printf("Wait for threads to finish\n");

+ 10 - 0
core/iwasm/libraries/lib-wasi-threads/tid_allocator.h

@@ -8,8 +8,14 @@
 
 
 #include "platform_common.h"
 #include "platform_common.h"
 
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #define TID_ALLOCATOR_INIT_SIZE CLUSTER_MAX_THREAD_NUM
 #define TID_ALLOCATOR_INIT_SIZE CLUSTER_MAX_THREAD_NUM
 enum {
 enum {
+    /* Keep it in sync with
+       https://github.com/WebAssembly/wasi-threads#design-choice-thread-ids */
     TID_MIN = 1,
     TID_MIN = 1,
     TID_MAX = 0x1FFFFFFF
     TID_MAX = 0x1FFFFFFF
 }; // Reserved TIDs (WASI specification)
 }; // Reserved TIDs (WASI specification)
@@ -33,4 +39,8 @@ tid_allocator_get_tid(TidAllocator *tid_allocator);
 void
 void
 tid_allocator_release_tid(TidAllocator *tid_allocator, int32 thread_id);
 tid_allocator_release_tid(TidAllocator *tid_allocator, int32 thread_id);
 
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* _TID_ALLOCATOR_H */
 #endif /* _TID_ALLOCATOR_H */

+ 6 - 0
core/iwasm/libraries/lib-wasi-threads/unit-test/lib_wasi_threads_unit_tests.cmake

@@ -0,0 +1,6 @@
+# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+create_wamr_unit_test(wasi_threads
+    ${CMAKE_CURRENT_LIST_DIR}/test_tid_allocator.cpp
+)

+ 62 - 0
core/iwasm/libraries/lib-wasi-threads/unit-test/test_tid_allocator.cpp

@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <gtest/gtest.h>
+
+#include "tid_allocator.h"
+
+#include <stdint.h>
+
+class TidAllocatorTest : public ::testing::Test
+{
+  protected:
+    void SetUp() override { ASSERT_TRUE(tid_allocator_init(&_allocator)); }
+
+    void TearDown() override { tid_allocator_deinit(&_allocator); }
+
+    TidAllocator _allocator;
+};
+
+static bool
+is_tid_valid(int32 tid)
+{
+    /* See: https://github.com/WebAssembly/wasi-threads#design-choice-thread-ids
+     */
+    return tid >= TID_MIN && tid <= TID_MAX;
+}
+
+TEST_F(TidAllocatorTest, BasicTest)
+{
+    int32 tid = tid_allocator_get_tid(&_allocator);
+
+    ASSERT_TRUE(is_tid_valid(tid));
+}
+
+TEST_F(TidAllocatorTest, ShouldFailOnAllocatingMoreThanAllowedThreadIDs)
+{
+    int32 last_tid = 0;
+    for (int32 i = 0; i < TID_MAX + 1; i++) {
+        last_tid = tid_allocator_get_tid(&_allocator);
+        if (last_tid < 0) {
+            break;
+        }
+        ASSERT_TRUE(is_tid_valid(last_tid));
+    }
+
+    ASSERT_LT(last_tid, 0);
+}
+
+TEST_F(TidAllocatorTest, ShouldAllocateMoreThanAllowedTIDsIfOldTIDsAreReleased)
+{
+    int32 last_tid = 0;
+    for (int32 i = 0; i < TID_MAX + 1; i++) {
+        if (last_tid != 0) {
+            tid_allocator_release_tid(&_allocator, last_tid);
+        }
+
+        last_tid = tid_allocator_get_tid(&_allocator);
+        ASSERT_TRUE(is_tid_valid(last_tid));
+    }
+}

+ 60 - 55
core/iwasm/libraries/thread-mgr/thread_manager.c

@@ -16,10 +16,6 @@
 #include "debug_engine.h"
 #include "debug_engine.h"
 #endif
 #endif
 
 
-#if WASM_ENABLE_SHARED_MEMORY != 0
-#include "wasm_shared_memory.h"
-#endif
-
 typedef struct {
 typedef struct {
     bh_list_link l;
     bh_list_link l;
     void (*destroy_cb)(WASMCluster *);
     void (*destroy_cb)(WASMCluster *);
@@ -32,6 +28,8 @@ static bh_list cluster_list_head;
 static bh_list *const cluster_list = &cluster_list_head;
 static bh_list *const cluster_list = &cluster_list_head;
 static korp_mutex cluster_list_lock;
 static korp_mutex cluster_list_lock;
 
 
+static korp_mutex _exception_lock;
+
 typedef void (*list_visitor)(void *, void *);
 typedef void (*list_visitor)(void *, void *);
 
 
 static uint32 cluster_max_thread_num = CLUSTER_MAX_THREAD_NUM;
 static uint32 cluster_max_thread_num = CLUSTER_MAX_THREAD_NUM;
@@ -52,6 +50,10 @@ thread_manager_init()
         return false;
         return false;
     if (os_mutex_init(&cluster_list_lock) != 0)
     if (os_mutex_init(&cluster_list_lock) != 0)
         return false;
         return false;
+    if (os_mutex_init(&_exception_lock) != 0) {
+        os_mutex_destroy(&cluster_list_lock);
+        return false;
+    }
     return true;
     return true;
 }
 }
 
 
@@ -66,6 +68,7 @@ thread_manager_destroy()
         cluster = next;
         cluster = next;
     }
     }
     wasm_cluster_cancel_all_callbacks();
     wasm_cluster_cancel_all_callbacks();
+    os_mutex_destroy(&_exception_lock);
     os_mutex_destroy(&cluster_list_lock);
     os_mutex_destroy(&cluster_list_lock);
 }
 }
 
 
@@ -1240,72 +1243,56 @@ wasm_cluster_resume_all(WASMCluster *cluster)
     os_mutex_unlock(&cluster->lock);
     os_mutex_unlock(&cluster->lock);
 }
 }
 
 
+struct spread_exception_data {
+    WASMExecEnv *skip;
+    const char *exception;
+};
+
 static void
 static void
 set_exception_visitor(void *node, void *user_data)
 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);
-    WASMModuleInstance *wasm_inst = (WASMModuleInstance *)module_inst;
-
-    if (curr_exec_env != exec_env) {
-        WASMModuleInstance *curr_wasm_inst =
-            (WASMModuleInstance *)get_module_inst(curr_exec_env);
-
-        /* Only spread non "wasi proc exit" exception */
-#if WASM_ENABLE_SHARED_MEMORY != 0
-        if (curr_wasm_inst->memory_count > 0)
-            shared_memory_lock(curr_wasm_inst->memories[0]);
-#endif
-        if (!strstr(wasm_inst->cur_exception, "wasi proc exit")) {
-            bh_memcpy_s(curr_wasm_inst->cur_exception,
-                        sizeof(curr_wasm_inst->cur_exception),
-                        wasm_inst->cur_exception,
-                        sizeof(wasm_inst->cur_exception));
+    const struct spread_exception_data *data = user_data;
+    WASMExecEnv *exec_env = (WASMExecEnv *)node;
+
+    if (exec_env != data->skip) {
+        WASMModuleInstance *wasm_inst =
+            (WASMModuleInstance *)get_module_inst(exec_env);
+
+        exception_lock(wasm_inst);
+        if (data->exception != NULL) {
+            /* Only spread non "wasi proc exit" exception */
+            if (strcmp(data->exception, "wasi proc exit")) {
+                snprintf(wasm_inst->cur_exception,
+                         sizeof(wasm_inst->cur_exception), "Exception: %s",
+                         data->exception);
+            }
         }
         }
-#if WASM_ENABLE_SHARED_MEMORY != 0
-        if (curr_wasm_inst->memory_count > 0)
-            shared_memory_unlock(curr_wasm_inst->memories[0]);
-#endif
+        else {
+            wasm_inst->cur_exception[0] = '\0';
+        }
+        exception_unlock(wasm_inst);
 
 
         /* Terminate the thread so it can exit from dead loops */
         /* Terminate the thread so it can exit from dead loops */
-        set_thread_cancel_flags(curr_exec_env);
-    }
-}
-
-static void
-clear_exception_visitor(void *node, void *user_data)
-{
-    WASMExecEnv *exec_env = (WASMExecEnv *)user_data;
-    WASMExecEnv *curr_exec_env = (WASMExecEnv *)node;
-
-    if (curr_exec_env != exec_env) {
-        WASMModuleInstance *curr_wasm_inst =
-            (WASMModuleInstance *)get_module_inst(curr_exec_env);
-
-#if WASM_ENABLE_SHARED_MEMORY != 0
-        if (curr_wasm_inst->memory_count > 0)
-            shared_memory_lock(curr_wasm_inst->memories[0]);
-#endif
-        curr_wasm_inst->cur_exception[0] = '\0';
-#if WASM_ENABLE_SHARED_MEMORY != 0
-        if (curr_wasm_inst->memory_count > 0)
-            shared_memory_unlock(curr_wasm_inst->memories[0]);
-#endif
+        if (data->exception != NULL) {
+            set_thread_cancel_flags(exec_env);
+        }
     }
     }
 }
 }
 
 
 void
 void
-wasm_cluster_spread_exception(WASMExecEnv *exec_env, bool clear)
+wasm_cluster_spread_exception(WASMExecEnv *exec_env, const char *exception)
 {
 {
+    const bool has_exception = exception != NULL;
     WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
     WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
     bh_assert(cluster);
     bh_assert(cluster);
 
 
+    struct spread_exception_data data;
+    data.skip = exec_env;
+    data.exception = exception;
+
     os_mutex_lock(&cluster->lock);
     os_mutex_lock(&cluster->lock);
-    cluster->has_exception = !clear;
-    traverse_list(&cluster->exec_env_list,
-                  clear ? clear_exception_visitor : set_exception_visitor,
-                  exec_env);
+    cluster->has_exception = has_exception;
+    traverse_list(&cluster->exec_env_list, set_exception_visitor, &data);
     os_mutex_unlock(&cluster->lock);
     os_mutex_unlock(&cluster->lock);
 }
 }
 
 
@@ -1353,3 +1340,21 @@ wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env)
 
 
     return is_thread_terminated;
     return is_thread_terminated;
 }
 }
+
+void
+exception_lock(WASMModuleInstance *module_inst)
+{
+    /*
+     * Note: this lock could be per module instance if desirable.
+     * We can revisit on AOT version bump.
+     * It probably doesn't matter though because the exception handling
+     * logic should not be executed too frequently anyway.
+     */
+    os_mutex_lock(&_exception_lock);
+}
+
+void
+exception_unlock(WASMModuleInstance *module_inst)
+{
+    os_mutex_unlock(&_exception_lock);
+}

+ 1 - 1
core/iwasm/libraries/thread-mgr/thread_manager.h

@@ -139,7 +139,7 @@ WASMExecEnv *
 wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst);
 wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst);
 
 
 void
 void
-wasm_cluster_spread_exception(WASMExecEnv *exec_env, bool clear);
+wasm_cluster_spread_exception(WASMExecEnv *exec_env, const char *exception);
 
 
 WASMExecEnv *
 WASMExecEnv *
 wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env);
 wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env);

+ 20 - 0
core/shared/platform/common/posix/posix_sleep.c

@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2023 Midokura Japan KK.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <time.h>
+
+#include "platform_api_extension.h"
+
+int
+os_usleep(uint32 usec)
+{
+    struct timespec ts;
+    int ret;
+
+    ts.tv_sec = usec / 1000000;
+    ts.tv_nsec = (usec % 1000000) * 1000;
+    ret = nanosleep(&ts, NULL);
+    return ret == 0 ? 0 : -1;
+}

+ 2 - 8
samples/wasi-threads/wasm-apps/no_pthread.c

@@ -50,16 +50,11 @@ main(int argc, char **argv)
     }
     }
 
 
     thread_id = __wasi_thread_spawn(&data);
     thread_id = __wasi_thread_spawn(&data);
-    if (thread_id < 0) {
-        printf("Failed to create thread: %d\n", thread_id);
-        ret = EXIT_FAILURE;
-        goto final;
-    }
+    ASSERT_VALID_TID(thread_id);
 
 
     if (__builtin_wasm_memory_atomic_wait32(&data.th_ready, 0, SECOND) == 2) {
     if (__builtin_wasm_memory_atomic_wait32(&data.th_ready, 0, SECOND) == 2) {
         printf("Timeout\n");
         printf("Timeout\n");
-        ret = EXIT_FAILURE;
-        goto final;
+        return EXIT_FAILURE;
     }
     }
 
 
     printf("Thread completed, new value: %d, thread id: %d\n", data.value,
     printf("Thread completed, new value: %d, thread id: %d\n", data.value,
@@ -67,7 +62,6 @@ main(int argc, char **argv)
 
 
     assert(thread_id == data.thread_id);
     assert(thread_id == data.thread_id);
 
 
-final:
     start_args_deinit(&data.base);
     start_args_deinit(&data.base);
 
 
     return ret;
     return ret;

+ 4 - 0
samples/wasi-threads/wasm-apps/wasi_thread_start.h

@@ -7,6 +7,10 @@
 
 
 #define STACK_SIZE 32 * 1024 // same as the main stack
 #define STACK_SIZE 32 * 1024 // same as the main stack
 
 
+/* See https://github.com/WebAssembly/wasi-threads#design-choice-thread-ids */
+#define ASSERT_VALID_TID(TID) \
+    assert(TID >= 1 && TID <= 0x1FFFFFFF && "Invalid thread ID")
+
 typedef struct {
 typedef struct {
     void *stack;
     void *stack;
 } start_args_t;
 } start_args_t;

+ 51 - 0
tests/unit/CMakeLists.txt

@@ -0,0 +1,51 @@
+# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+cmake_minimum_required (VERSION 3.14)
+
+project (wamr_unit_tests)
+
+include (CTest)
+
+if (NOT DEFINED WAMR_BUILD_INTERP)
+  # Enable Interpreter by default
+  set (WAMR_BUILD_INTERP 1)
+endif ()
+
+if (NOT DEFINED WAMR_BUILD_PLATFORM)
+  string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM)
+endif ()
+
+set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
+include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
+add_library (vmlib ${WAMR_RUNTIME_LIB_SOURCE})
+
+include (FetchContent)
+FetchContent_Declare (
+    googletest
+    URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
+)
+# For Windows: Prevent overriding the parent project's compiler/linker settings
+set (gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable (googletest)
+
+include (GoogleTest)
+
+add_library (wamr_gtest_main main.cpp)
+target_link_libraries (wamr_gtest_main PUBLIC gtest vmlib)
+
+function (create_wamr_unit_test test_name)
+    set (sources ${ARGN})
+    add_executable (${test_name} ${sources})
+    target_link_libraries (
+        ${test_name}
+        wamr_gtest_main
+        vmlib
+        ${LLVM_AVAILABLE_LIBS}
+    )
+    gtest_discover_tests (${test_name})
+    endfunction ()
+
+if (WAMR_BUILD_LIB_WASI_THREADS EQUAL 1)
+    include (${IWASM_DIR}/libraries/lib-wasi-threads/unit-test/lib_wasi_threads_unit_tests.cmake)
+endif ()

+ 21 - 0
tests/unit/main.cpp

@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+#include <gtest/gtest.h>
+#include "wasm_runtime_common.h"
+
+int
+main(int argc, char **argv)
+{
+    ::testing::InitGoogleTest(&argc, argv);
+
+    if (!wasm_runtime_init()) {
+        return -1;
+    }
+
+    int ret = RUN_ALL_TESTS();
+    wasm_runtime_destroy();
+
+    return ret;
+}

+ 19 - 27
tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh

@@ -15,25 +15,22 @@ readonly IWASM_CMD="${WORK_DIR}/../../../../product-mini/platforms/${PLATFORM}/b
     --allow-resolve=google-public-dns-a.google.com \
     --allow-resolve=google-public-dns-a.google.com \
     --addr-pool=::1/128,127.0.0.1/32"
     --addr-pool=::1/128,127.0.0.1/32"
 
 
-readonly IWASM_CMD_STRESS="${IWASM_CMD} --max-threads=8"
+readonly IWASM_CMD_STRESS="${IWASM_CMD} --max-threads=12"
 readonly WAMRC_CMD="${WORK_DIR}/../../../../wamr-compiler/build/wamrc"
 readonly WAMRC_CMD="${WORK_DIR}/../../../../wamr-compiler/build/wamrc"
 readonly C_TESTS="tests/c/testsuite/"
 readonly C_TESTS="tests/c/testsuite/"
 readonly ASSEMBLYSCRIPT_TESTS="tests/assemblyscript/testsuite/"
 readonly ASSEMBLYSCRIPT_TESTS="tests/assemblyscript/testsuite/"
 readonly THREAD_PROPOSAL_TESTS="tests/proposals/wasi-threads/"
 readonly THREAD_PROPOSAL_TESTS="tests/proposals/wasi-threads/"
 readonly THREAD_INTERNAL_TESTS="${WAMR_DIR}/core/iwasm/libraries/lib-wasi-threads/test/"
 readonly THREAD_INTERNAL_TESTS="${WAMR_DIR}/core/iwasm/libraries/lib-wasi-threads/test/"
+readonly THREAD_STRESS_TESTS="${WAMR_DIR}/core/iwasm/libraries/lib-wasi-threads/stress-test/"
 readonly LIB_SOCKET_TESTS="${WAMR_DIR}/core/iwasm/libraries/lib-socket/test/"
 readonly LIB_SOCKET_TESTS="${WAMR_DIR}/core/iwasm/libraries/lib-socket/test/"
-readonly STRESS_TESTS=("spawn_stress_test.wasm" "stress_test_threads_creation.wasm")
 
 
 run_aot_tests () {
 run_aot_tests () {
     local tests=("$@")
     local tests=("$@")
-    local iwasm="${IWASM_CMD}"
     for test_wasm in ${tests[@]}; do
     for test_wasm in ${tests[@]}; do
-        local extra_stress_flags=""
-        for stress_test in "${STRESS_TESTS[@]}"; do
-            if [ "$test_wasm" == "$stress_test" ]; then
-                iwasm="${IWASM_CMD_STRESS}"
-            fi  
-        done
+        local iwasm="${IWASM_CMD}"
+        if [[ $test_wasm =~ "stress" ]]; then
+            iwasm="${IWASM_CMD_STRESS}"
+        fi
 
 
         test_aot="${test_wasm%.wasm}.aot"
         test_aot="${test_wasm%.wasm}.aot"
         test_json="${test_wasm%.wasm}.json"
         test_json="${test_wasm%.wasm}.json"
@@ -52,7 +49,7 @@ run_aot_tests () {
             expected=$(jq .exit_code ${test_json})
             expected=$(jq .exit_code ${test_json})
         fi
         fi
 
 
-        ${IWASM_CMD} $extra_stress_flags $test_aot
+        ${iwasm} $test_aot
         ret=${PIPESTATUS[0]}
         ret=${PIPESTATUS[0]}
 
 
         echo "expected=$expected, actual=$ret"
         echo "expected=$expected, actual=$ret"
@@ -66,19 +63,6 @@ if [[ $MODE != "aot" ]];then
     python3 -m venv wasi-env && source wasi-env/bin/activate
     python3 -m venv wasi-env && source wasi-env/bin/activate
     python3 -m pip install -r test-runner/requirements.txt
     python3 -m pip install -r test-runner/requirements.txt
 
 
-    # Stress tests require max-threads=8 so they're executed separately
-    for stress_test in "${STRESS_TESTS[@]}"; do
-        if [[ -e "${THREAD_INTERNAL_TESTS}${stress_test}" ]]; then
-            echo "${stress_test}" is a stress test
-            ${IWASM_CMD_STRESS} ${THREAD_INTERNAL_TESTS}${stress_test}
-            ret=${PIPESTATUS[0]}
-            if [ "${ret}" -ne 0 ]; then
-                echo "Stress test ${stress_test} FAILED with code " ${ret}
-                exit_code=${ret}
-            fi
-        fi
-    done
-
     TEST_RUNTIME_EXE="${IWASM_CMD}" python3 test-runner/wasi_test_runner.py \
     TEST_RUNTIME_EXE="${IWASM_CMD}" python3 test-runner/wasi_test_runner.py \
             -r adapters/wasm-micro-runtime.py \
             -r adapters/wasm-micro-runtime.py \
             -t \
             -t \
@@ -87,12 +71,20 @@ if [[ $MODE != "aot" ]];then
                 ${THREAD_PROPOSAL_TESTS} \
                 ${THREAD_PROPOSAL_TESTS} \
                 ${THREAD_INTERNAL_TESTS} \
                 ${THREAD_INTERNAL_TESTS} \
                 ${LIB_SOCKET_TESTS} \
                 ${LIB_SOCKET_TESTS} \
-            --exclude-filter "${THREAD_INTERNAL_TESTS}skip.json"
 
 
     ret=${PIPESTATUS[0]}
     ret=${PIPESTATUS[0]}
-    if [ "${ret}" -ne 0 ]; then
-        exit_code=${ret}
+
+    TEST_RUNTIME_EXE="${IWASM_CMD_STRESS}" python3 test-runner/wasi_test_runner.py \
+            -r adapters/wasm-micro-runtime.py \
+            -t \
+                ${THREAD_STRESS_TESTS}
+
+    if [ "${ret}" -eq 0 ]; then
+        ret=${PIPESTATUS[0]}
     fi
     fi
+    
+    exit_code=${ret}
+    
     deactivate
     deactivate
 else
 else
     target_option=""
     target_option=""
@@ -101,7 +93,7 @@ else
     fi
     fi
 
 
     exit_code=0
     exit_code=0
-    for testsuite in ${THREAD_PROPOSAL_TESTS} ${THREAD_INTERNAL_TESTS}; do
+    for testsuite in ${THREAD_STRESS_TESTS} ${THREAD_PROPOSAL_TESTS} ${THREAD_INTERNAL_TESTS}; do
         tests=$(ls ${testsuite}*.wasm)
         tests=$(ls ${testsuite}*.wasm)
         tests_array=($tests)
         tests_array=($tests)
         run_aot_tests "${tests_array[@]}"
         run_aot_tests "${tests_array[@]}"