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

Add internal tests for WASI threads (#1963)

Add internal tests for WASI threads. These tests are run in addition to
the ones in the proposal:
https://github.com/WebAssembly/wasi-threads/tree/main/test/testsuite.

The purpose is to test additional and more complex scenarios.
Enrico Loparco 2 лет назад
Родитель
Сommit
128c0ea899
36 измененных файлов с 864 добавлено и 11 удалено
  1. 34 0
      .github/workflows/compilation_on_android_ubuntu.yml
  2. 3 0
      .gitignore
  3. 30 0
      core/iwasm/libraries/lib-wasi-threads/test/build.sh
  4. 122 0
      core/iwasm/libraries/lib-wasi-threads/test/common.h
  5. 128 0
      core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c
  6. 70 0
      core/iwasm/libraries/lib-wasi-threads/test/global_atomic.c
  7. 78 0
      core/iwasm/libraries/lib-wasi-threads/test/global_lock.c
  8. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.c
  9. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.json
  10. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.c
  11. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.json
  12. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.c
  13. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.json
  14. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.c
  15. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.json
  16. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.c
  17. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.json
  18. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.c
  19. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.json
  20. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.c
  21. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.json
  22. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.c
  23. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.json
  24. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.c
  25. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.json
  26. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.c
  27. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.json
  28. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.c
  29. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.json
  30. 16 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.c
  31. 3 0
      core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.json
  32. 72 0
      core/iwasm/libraries/lib-wasi-threads/test/spawn_multiple_times.c
  33. 86 0
      core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c
  34. 1 1
      samples/wasi-threads/wasm-apps/no_pthread.c
  35. 9 9
      samples/wasi-threads/wasm-apps/thread_termination.c
  36. 3 1
      tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh

+ 34 - 0
.github/workflows/compilation_on_android_ubuntu.yml

@@ -457,6 +457,10 @@ jobs:
             $THREADS_TEST_OPTIONS,
             $WASI_TEST_OPTIONS,
           ]
+        wasi_sdk_release:
+          [
+            "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz",
+          ]
         llvm_cache_key:
           ["${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }}"]
         exclude:
@@ -493,6 +497,31 @@ jobs:
       - name: checkout
         uses: actions/checkout@v3
 
+      - name: download and install wasi-sdk
+        if: matrix.test_option == '$WASI_TEST_OPTIONS'
+        run: |
+          cd /opt
+          sudo wget ${{ matrix.wasi_sdk_release }}
+          sudo tar -xzf wasi-sdk-*.tar.gz
+          sudo mv wasi-sdk-19.0 wasi-sdk
+
+      - name: build wasi-libc (needed for wasi-threads)
+        if: matrix.test_option == '$WASI_TEST_OPTIONS'
+        run: |
+          mkdir wasi-libc
+          cd wasi-libc
+          git init
+          # "Rename thread_spawn import" commit on main branch
+          git fetch https://github.com/WebAssembly/wasi-libc \
+            8f5275796a82f8ecfd0833a4f3f444fa37ed4546
+          git checkout FETCH_HEAD
+          make \
+            AR=/opt/wasi-sdk/bin/llvm-ar \
+            NM=/opt/wasi-sdk/bin/llvm-nm \
+            CC=/opt/wasi-sdk/bin/clang \
+            THREAD_MODEL=posix
+        working-directory: core/deps
+
       - name: set env variable(if llvm are used)
         if: matrix.running_mode == 'aot' || matrix.running_mode == 'jit' || matrix.running_mode == 'multi-tier-jit'
         run: echo "USE_LLVM=true" >> $GITHUB_ENV
@@ -526,6 +555,11 @@ jobs:
         if: matrix.running_mode == 'aot' && matrix.test_option == '$WASI_TEST_OPTIONS'
         run: sudo apt-get update && sudo apt install -y jq
 
+      - name: Build WASI thread tests
+        if: matrix.test_option == '$WASI_TEST_OPTIONS'
+        run: WASI_SYSROOT=../../../../../core/deps/wasi-libc/sysroot bash build.sh
+        working-directory: ./core/iwasm/libraries/lib-wasi-threads/test/
+
       - name: run tests
         timeout-minutes: 10
         run: ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }}

+ 3 - 0
.gitignore

@@ -13,6 +13,7 @@
 core/deps/**
 core/shared/mem-alloc/tlsf
 core/app-framework/wgl
+core/iwasm/libraries/lib-wasi-threads/test/*.wasm
 
 wamr-sdk/out/
 wamr-sdk/runtime/build_runtime_sdk/
@@ -35,3 +36,5 @@ tests/benchmarks/coremark/coremark*
 
 samples/workload/include/**
 !samples/workload/include/.gitkeep
+
+# core/iwasm/libraries/wasi-threads

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

@@ -0,0 +1,30 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+
+CC=${CC:=/opt/wasi-sdk/bin/clang}
+WASI_SYSROOT=${WASI_SYSROOT:=~/dev/wasi-libc/sysroot}
+WAMR_DIR=../../../../..
+
+for test_c in *.c; do
+    test_wasm="$(basename $test_c .c).wasm"
+
+    echo "Compiling $test_c to $test_wasm"
+    $CC \
+        --sysroot $WASI_SYSROOT \
+        -target wasm32-wasi-threads \
+        -pthread -ftls-model=local-exec \
+        -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 \
+        -I $WAMR_DIR/samples/wasi-threads/wasm-apps \
+        $WAMR_DIR/samples/wasi-threads/wasm-apps/wasi_thread_start.S \
+        $test_c -o $test_wasm
+done

+ 122 - 0
core/iwasm/libraries/lib-wasi-threads/test/common.h

@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "wasi_thread_start.h"
+
+typedef enum {
+    BLOCKING_TASK_BUSY_WAIT,
+    BLOCKING_TASK_ATOMIC_WAIT,
+    BLOCKING_TASK_POLL_ONEOFF
+} blocking_task_type_t;
+
+/* Parameter to change test behavior */
+static bool termination_by_trap;
+static bool termination_in_main_thread;
+static blocking_task_type_t blocking_task_type;
+
+#define TIMEOUT_SECONDS 10ll
+#define NUM_THREADS 3
+static pthread_barrier_t barrier;
+
+typedef struct {
+    start_args_t base;
+    bool throw_exception;
+} shared_t;
+
+void
+run_long_task()
+{
+    if (blocking_task_type == BLOCKING_TASK_BUSY_WAIT) {
+        for (int i = 0; i < TIMEOUT_SECONDS; i++)
+            sleep(1);
+    }
+    else if (blocking_task_type == BLOCKING_TASK_ATOMIC_WAIT) {
+        __builtin_wasm_memory_atomic_wait32(
+            0, 0, TIMEOUT_SECONDS * 1000 * 1000 * 1000);
+    }
+    else {
+        sleep(TIMEOUT_SECONDS);
+    }
+}
+
+void
+start_job()
+{
+    /* Wait for all threads (including the main thread) to be ready */
+    pthread_barrier_wait(&barrier);
+    run_long_task(); /* Task to be interrupted */
+    assert(false && "Thread termination test failed");
+}
+
+void
+terminate_process()
+{
+    /* Wait for all threads (including the main thread) to be ready */
+    pthread_barrier_wait(&barrier);
+
+    if (termination_by_trap)
+        __builtin_trap();
+    else
+        __wasi_proc_exit(33);
+}
+
+void
+__wasi_thread_start_C(int thread_id, int *start_arg)
+{
+    shared_t *data = (shared_t *)start_arg;
+
+    if (data->throw_exception) {
+        terminate_process();
+    }
+    else {
+        start_job();
+    }
+}
+
+void
+test_termination(bool trap, bool main, blocking_task_type_t task_type)
+{
+    termination_by_trap = trap;
+    termination_in_main_thread = main;
+    blocking_task_type = task_type;
+
+    int thread_id = -1, i;
+    shared_t data[NUM_THREADS] = { 0 };
+    assert(pthread_barrier_init(&barrier, NULL, NUM_THREADS + 1) == 0
+           && "Failed to init barrier");
+
+    for (i = 0; i < NUM_THREADS; i++) {
+        /* No graceful memory free to simplify the test */
+        assert(start_args_init(&data[i].base)
+               && "Failed to allocate thread's stack");
+    }
+
+    /* Create a thread that forces termination through trap or `proc_exit` */
+    data[0].throw_exception = !termination_in_main_thread;
+    thread_id = __wasi_thread_spawn(&data[0]);
+    assert(thread_id > 0 && "Failed to create thread");
+
+    /* Create two additional threads to test exception propagation */
+    data[1].throw_exception = false;
+    thread_id = __wasi_thread_spawn(&data[1]);
+    assert(thread_id > 0 && "Failed to create thread");
+    data[2].throw_exception = false;
+    thread_id = __wasi_thread_spawn(&data[2]);
+    assert(thread_id > 0 && "Failed to create thread");
+
+    if (termination_in_main_thread) {
+        terminate_process();
+    }
+    else {
+        start_job();
+    }
+}

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

@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include "wasi_thread_start.h"
+
+enum CONSTANTS {
+    MAX_NUM_THREADS = 4, /* Should be the same as "--max-threads" */
+    NUM_RETRY = 5,
+    SECOND = 1000 * 1000 * 1000, /* 1 second */
+    TIMEOUT = 10LL * SECOND
+};
+
+int g_count = 0;
+
+typedef struct {
+    start_args_t base;
+    int th_ready;
+    int th_continue;
+    int th_done;
+    bool no_ops;
+} shared_t;
+
+void
+__wasi_thread_start_C(int thread_id, int *start_arg)
+{
+    shared_t *data = (shared_t *)start_arg;
+
+    if (data->no_ops) {
+        __builtin_wasm_memory_atomic_wait32(NULL, 0, 2 * SECOND);
+        return;
+    }
+
+    __atomic_store_n(&data->th_ready, 1, __ATOMIC_SEQ_CST);
+    __builtin_wasm_memory_atomic_notify(&data->th_ready, 1);
+
+    if (__builtin_wasm_memory_atomic_wait32(&data->th_continue, 0, TIMEOUT)
+        == 2) {
+        assert(false && "Wait should not time out");
+    }
+
+    __atomic_fetch_add(&g_count, 1, __ATOMIC_SEQ_CST);
+
+    __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST);
+    __builtin_wasm_memory_atomic_notify(&data->th_done, 1);
+}
+
+int
+main(int argc, char **argv)
+{
+    shared_t data[MAX_NUM_THREADS] = { 0 };
+    int thread_ids[MAX_NUM_THREADS];
+
+    for (int i = 0; i < MAX_NUM_THREADS; i++) {
+        assert(start_args_init(&data[i].base));
+        thread_ids[i] = __wasi_thread_spawn(&data[i]);
+        printf("Thread created with id=%d\n", thread_ids[i]);
+        assert(thread_ids[i] > 0 && "Thread creation failed");
+
+        for (int j = 0; j < i; j++) {
+            assert(thread_ids[i] != thread_ids[j] && "Duplicated TIDs");
+        }
+
+        if (__builtin_wasm_memory_atomic_wait32(&data[i].th_ready, 0, TIMEOUT)
+            == 2) {
+            assert(false && "Wait should not time out");
+        }
+    }
+
+    printf("Attempt to create thread when not possible\n");
+    shared_t data_fail = { 0 };
+    assert(start_args_init(&data_fail.base));
+    int thread_id = __wasi_thread_spawn(&data_fail);
+    start_args_deinit(&data_fail.base);
+    assert(thread_id < 0 && "Thread creation should fail");
+
+    printf("Unlock created threads\n");
+    for (int i = 0; i < MAX_NUM_THREADS; i++) {
+        __atomic_store_n(&data[i].th_continue, 1, __ATOMIC_SEQ_CST);
+        __builtin_wasm_memory_atomic_notify(&data[i].th_continue, 1);
+    }
+
+    printf("Wait for threads to finish\n");
+    for (int i = 0; i < MAX_NUM_THREADS; i++) {
+        if (__builtin_wasm_memory_atomic_wait32(&data[i].th_done, 0, TIMEOUT)
+            == 2) {
+            assert(false && "Wait should not time out");
+        }
+
+        start_args_deinit(&data[i].base);
+    }
+
+    printf("Value of count after update: %d\n", g_count);
+    assert(g_count == (MAX_NUM_THREADS)
+           && "Global count not updated correctly");
+
+    /* --------------------------------------------------- */
+
+    printf("Create new threads without waiting from them to finish\n");
+    shared_t data_no_join[MAX_NUM_THREADS] = { 0 };
+    for (int i = 0; i < MAX_NUM_THREADS; i++) {
+        /* No graceful memory free to simplify the test */
+        assert(start_args_init(&data_no_join[i].base));
+        data_no_join[i].no_ops = true;
+
+        int thread_id = -1;
+        for (int j = 0; j < NUM_RETRY && thread_id < 0; j++) {
+            thread_id = __wasi_thread_spawn(&data_no_join[i]);
+            if (thread_id < 0)
+                __builtin_wasm_memory_atomic_wait32(NULL, 0, SECOND);
+        }
+
+        printf("Thread created with id=%d\n", thread_id);
+        assert(thread_id > 0 && "Thread creation should succeed");
+    }
+
+    return EXIT_SUCCESS;
+}

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

@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include "wasi_thread_start.h"
+
+enum CONSTANTS {
+    NUM_THREADS = 4,
+    NUM_ITER = 1000,
+    SECOND = 1000 * 1000 * 1000, /* 1 second */
+    TIMEOUT = 10LL * SECOND
+};
+
+int g_count = 0;
+
+typedef struct {
+    start_args_t base;
+    int th_done;
+} shared_t;
+
+void
+__wasi_thread_start_C(int thread_id, int *start_arg)
+{
+    shared_t *data = (shared_t *)start_arg;
+
+    for (int i = 0; i < NUM_ITER; i++)
+        __atomic_fetch_add(&g_count, 1, __ATOMIC_SEQ_CST);
+
+    __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST);
+    __builtin_wasm_memory_atomic_notify(&data->th_done, 1);
+}
+
+int
+main(int argc, char **argv)
+{
+    shared_t data[NUM_THREADS] = { 0 };
+    int thread_ids[NUM_THREADS];
+
+    for (int i = 0; i < NUM_THREADS; i++) {
+        assert(start_args_init(&data[i].base));
+        thread_ids[i] = __wasi_thread_spawn(&data[i]);
+        assert(thread_ids[i] > 0 && "Thread creation failed");
+    }
+
+    printf("Wait for threads to finish\n");
+    for (int i = 0; i < NUM_THREADS; i++) {
+        if (__builtin_wasm_memory_atomic_wait32(&data[i].th_done, 0, TIMEOUT)
+            == 2) {
+            assert(false && "Wait should not time out");
+        }
+
+        start_args_deinit(&data[i].base);
+    }
+
+    printf("Value of count after update: %d\n", g_count);
+    assert(g_count == (NUM_THREADS * NUM_ITER)
+           && "Global count not updated correctly");
+
+    return EXIT_SUCCESS;
+}

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

@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include "wasi_thread_start.h"
+
+enum CONSTANTS {
+    NUM_THREADS = 4,
+    NUM_ITER = 200,
+    SECOND = 1000 * 1000 * 1000, /* 1 second */
+    TIMEOUT = 10LL * SECOND
+};
+
+pthread_mutex_t mutex;
+int g_count = 0;
+
+typedef struct {
+    start_args_t base;
+    int th_done;
+} shared_t;
+
+void
+__wasi_thread_start_C(int thread_id, int *start_arg)
+{
+    shared_t *data = (shared_t *)start_arg;
+
+    for (int i = 0; i < NUM_ITER; i++) {
+        pthread_mutex_lock(&mutex);
+        g_count++;
+        pthread_mutex_unlock(&mutex);
+    }
+
+    __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST);
+    __builtin_wasm_memory_atomic_notify(&data->th_done, 1);
+}
+
+int
+main(int argc, char **argv)
+{
+    shared_t data[NUM_THREADS] = { 0 };
+    int thread_ids[NUM_THREADS];
+
+    assert(pthread_mutex_init(&mutex, NULL) == 0 && "Failed to init mutex");
+
+    for (int i = 0; i < NUM_THREADS; i++) {
+        assert(start_args_init(&data[i].base));
+        thread_ids[i] = __wasi_thread_spawn(&data[i]);
+        assert(thread_ids[i] > 0 && "Thread creation failed");
+    }
+
+    printf("Wait for threads to finish\n");
+    for (int i = 0; i < NUM_THREADS; i++) {
+        if (__builtin_wasm_memory_atomic_wait32(&data[i].th_done, 0, TIMEOUT)
+            == 2) {
+            assert(false && "Wait should not time out");
+        }
+
+        start_args_deinit(&data[i].base);
+    }
+
+    printf("Value of count after update: %d\n", g_count);
+    assert(g_count == (NUM_THREADS * NUM_ITER)
+           && "Global count not updated correctly");
+
+    assert(pthread_mutex_destroy(&mutex) == 0 && "Failed to destroy mutex");
+    return EXIT_SUCCESS;
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(false, true, BLOCKING_TASK_BUSY_WAIT);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 33
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(false, true, BLOCKING_TASK_POLL_ONEOFF);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 33
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(false, true, BLOCKING_TASK_ATOMIC_WAIT);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 33
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(true, true, BLOCKING_TASK_BUSY_WAIT);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 1
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(true, true, BLOCKING_TASK_POLL_ONEOFF);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 1
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(true, true, BLOCKING_TASK_ATOMIC_WAIT);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 1
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(false, false, BLOCKING_TASK_BUSY_WAIT);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 33
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(false, false, BLOCKING_TASK_POLL_ONEOFF);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 33
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(false, false, BLOCKING_TASK_ATOMIC_WAIT);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 33
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(true, false, BLOCKING_TASK_BUSY_WAIT);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 1
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(true, false, BLOCKING_TASK_POLL_ONEOFF);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 1
+}

+ 16 - 0
core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.c

@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+    test_termination(true, false, BLOCKING_TASK_ATOMIC_WAIT);
+}

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

@@ -0,0 +1,3 @@
+{
+    "exit_code": 1
+}

+ 72 - 0
core/iwasm/libraries/lib-wasi-threads/test/spawn_multiple_times.c

@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include "wasi_thread_start.h"
+
+enum CONSTANTS {
+    NUM_ITER = 50,
+    NUM_RETRY = 5,
+    SECOND = 1000 * 1000 * 1000, /* 1 second */
+    TIMEOUT = 5LL * SECOND
+};
+
+typedef struct {
+    start_args_t base;
+    int th_done;
+} shared_t;
+
+int g_count = 0;
+
+void
+__wasi_thread_start_C(int thread_id, int *start_arg)
+{
+    shared_t *data = (shared_t *)start_arg;
+
+    g_count++;
+
+    __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST);
+    __builtin_wasm_memory_atomic_notify(&data->th_done, 1);
+}
+
+int
+main(int argc, char **argv)
+{
+    shared_t data = { 0 };
+    assert(start_args_init(&data.base) && "Stack allocation for thread failed");
+
+    for (int i = 0; i < NUM_ITER; i++) {
+        data.th_done = 0;
+
+        printf("Creating thread\n");
+        int thread_id = -1;
+        for (int j = 0; j < NUM_RETRY && thread_id < 0; j++) {
+            thread_id = __wasi_thread_spawn(&data);
+            if (thread_id < 0)
+                __builtin_wasm_memory_atomic_wait32(NULL, 0, SECOND);
+        }
+        assert(thread_id > 0 && "Thread creation should succeed");
+
+        printf("Waiting for thread to finish\n");
+        if (__builtin_wasm_memory_atomic_wait32(&data.th_done, 0, TIMEOUT)
+            == 2) {
+            assert(false && "Wait should not time out");
+        }
+        printf("Thread has finished\n");
+    }
+
+    assert(g_count == NUM_ITER && "Count has not been updated correctly");
+
+    start_args_deinit(&data.base);
+    return EXIT_SUCCESS;
+}

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

@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef __wasi__
+#error This example only compiles to WASM/WASI target
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include "wasi_thread_start.h"
+
+enum CONSTANTS {
+    NUM_THREADS = 4,
+    NUM_ITER = 30,
+    SECOND = 1000 * 1000 * 1000, /* 1 second */
+    TIMEOUT = 10LL * SECOND
+};
+
+typedef struct {
+    start_args_t base;
+    int th_done;
+    int *count;
+    int iteration;
+    int *pval;
+} shared_t;
+
+int *vals[NUM_THREADS];
+
+void
+__wasi_thread_start_C(int thread_id, int *start_arg)
+{
+    shared_t *data = (shared_t *)start_arg;
+
+    for (int i = 0; i < NUM_ITER; i++)
+        __atomic_fetch_add(data->count, 1, __ATOMIC_SEQ_CST);
+
+    vals[data->iteration] = malloc(sizeof(int));
+    *vals[data->iteration] = data->iteration;
+
+    __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST);
+    __builtin_wasm_memory_atomic_notify(&data->th_done, 1);
+}
+
+int
+main(int argc, char **argv)
+{
+    shared_t data[NUM_THREADS] = { 0 };
+    int thread_ids[NUM_THREADS];
+    int *count = calloc(1, sizeof(int));
+
+    for (int i = 0; i < NUM_THREADS; i++) {
+        assert(start_args_init(&data[i].base)
+               && "Stack allocation for thread failed");
+        __atomic_store_n(&data[i].count, count, __ATOMIC_SEQ_CST);
+        data[i].iteration = i;
+
+        thread_ids[i] = __wasi_thread_spawn(&data[i]);
+        assert(thread_ids[i] > 0 && "Thread creation failed");
+    }
+
+    printf("Wait for threads to finish\n");
+    for (int i = 0; i < NUM_THREADS; i++) {
+        if (__builtin_wasm_memory_atomic_wait32(&data[i].th_done, 0, TIMEOUT)
+            == 2) {
+            assert(false && "Wait should not time out");
+        }
+
+        start_args_deinit(&data[i].base);
+    }
+
+    assert(*count == (NUM_THREADS * NUM_ITER) && "Count not updated correctly");
+
+    for (int i = 0; i < NUM_THREADS; i++) {
+        printf("val=%d\n", *vals[i]);
+        assert(*vals[i] == i && "Value not updated correctly");
+        free(vals[i]);
+    }
+
+    free(count);
+    return EXIT_SUCCESS;
+}

+ 1 - 1
samples/wasi-threads/wasm-apps/no_pthread.c

@@ -33,7 +33,7 @@ __wasi_thread_start_C(int thread_id, int *start_arg)
     data->value += 8;
     printf("Updated value: %d\n", data->value);
 
-    data->th_ready = 1;
+    __atomic_store_n(&data->th_ready, 1, __ATOMIC_SEQ_CST);
     __builtin_wasm_memory_atomic_notify(&data->th_ready, 1);
 }
 

+ 9 - 9
samples/wasi-threads/wasm-apps/thread_termination.c

@@ -9,7 +9,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <assert.h>
-#include <semaphore.h>
+#include <pthread.h>
 #include <stdbool.h>
 #include <unistd.h>
 
@@ -26,7 +26,7 @@
 
 #define TIMEOUT_SECONDS 10
 #define NUM_THREADS 3
-static sem_t sem;
+static pthread_barrier_t barrier;
 
 typedef struct {
     start_args_t base;
@@ -49,9 +49,10 @@ run_long_task()
 void
 start_job()
 {
-    sem_post(&sem);
-    run_long_task(); /* Wait to be interrupted */
-    assert(false && "Unreachable");
+    /* Wait for all threads (including the main thread) to be ready */
+    pthread_barrier_wait(&barrier);
+    run_long_task(); /* Task to be interrupted */
+    assert(false && "Thread termination test failed");
 }
 
 void
@@ -59,8 +60,7 @@ terminate_process()
 {
     /* Wait for all other threads (including main thread) to be ready */
     printf("Waiting before terminating\n");
-    for (int i = 0; i < NUM_THREADS; i++)
-        sem_wait(&sem);
+    pthread_barrier_wait(&barrier);
 
     printf("Force termination\n");
 #if TEST_TERMINATION_BY_TRAP == 1
@@ -91,8 +91,8 @@ main(int argc, char **argv)
     int thread_id = -1, i;
     shared_t data[NUM_THREADS] = { 0 };
 
-    if (sem_init(&sem, 0, 0) != 0) {
-        printf("Failed to init semaphore\n");
+    if (pthread_barrier_init(&barrier, NULL, NUM_THREADS + 1) != 0) {
+        printf("Failed to init barrier\n");
         return EXIT_FAILURE;
     }
 

+ 3 - 1
tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh

@@ -10,6 +10,7 @@ readonly TARGET=$2
 
 readonly WORK_DIR=$PWD
 readonly PLATFORM=$(uname -s | tr A-Z a-z)
+readonly WAMR_DIR="${WORK_DIR}/../../../.."
 readonly IWASM_CMD="${WORK_DIR}/../../../../product-mini/platforms/${PLATFORM}/build/iwasm"
 readonly WAMRC_CMD="${WORK_DIR}/../../../../wamr-compiler/build/wamrc"
 
@@ -21,7 +22,8 @@ if [[ $MODE != "aot" ]];then
                 -t \
                     tests/c/testsuite/ \
                     tests/assemblyscript/testsuite/ \
-                    tests/proposals/wasi-threads/
+                    tests/proposals/wasi-threads/ \
+                    ${WAMR_DIR}/core/iwasm/libraries/lib-wasi-threads/test/
     exit_code=${PIPESTATUS[0]}
     deactivate
 else