diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index 81245307..9d52cac4 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.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 }} diff --git a/.gitignore b/.gitignore index 8b4dce9b..719972ef 100644 --- a/.gitignore +++ b/.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 \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/build.sh b/core/iwasm/libraries/lib-wasi-threads/test/build.sh new file mode 100644 index 00000000..58ff5948 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/common.h b/core/iwasm/libraries/lib-wasi-threads/test/common.h new file mode 100644 index 00000000..a531e39d --- /dev/null +++ b/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 +#include +#include +#include +#include +#include + +#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(); + } +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c b/core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c new file mode 100644 index 00000000..23ba5f62 --- /dev/null +++ b/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 +#include +#include +#include + +#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; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/global_atomic.c b/core/iwasm/libraries/lib-wasi-threads/test/global_atomic.c new file mode 100644 index 00000000..a38e7536 --- /dev/null +++ b/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 +#include +#include +#include + +#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; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/global_lock.c b/core/iwasm/libraries/lib-wasi-threads/test/global_lock.c new file mode 100644 index 00000000..282bec71 --- /dev/null +++ b/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 +#include +#include +#include +#include + +#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; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.c b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.c new file mode 100644 index 00000000..19d3ec25 --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.json b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.json new file mode 100644 index 00000000..5370f667 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.c b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.c new file mode 100644 index 00000000..a667e912 --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.json b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.json new file mode 100644 index 00000000..5370f667 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.c b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.c new file mode 100644 index 00000000..dc8615ad --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.json b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.json new file mode 100644 index 00000000..5370f667 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.c b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.c new file mode 100644 index 00000000..bb0ac8fa --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.json b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.json new file mode 100644 index 00000000..07689a10 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.c b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.c new file mode 100644 index 00000000..a2c24888 --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.json b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.json new file mode 100644 index 00000000..07689a10 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.c b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.c new file mode 100644 index 00000000..0904f34b --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.json b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.json new file mode 100644 index 00000000..07689a10 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.c new file mode 100644 index 00000000..71fdcb81 --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.json new file mode 100644 index 00000000..5370f667 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.c new file mode 100644 index 00000000..14352cf4 --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.json new file mode 100644 index 00000000..5370f667 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.c new file mode 100644 index 00000000..0963aa02 --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.json new file mode 100644 index 00000000..5370f667 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.c new file mode 100644 index 00000000..b3e3af7d --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.json new file mode 100644 index 00000000..07689a10 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.c new file mode 100644 index 00000000..a68ae8be --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.json new file mode 100644 index 00000000..07689a10 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.c new file mode 100644 index 00000000..52c684a5 --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.json new file mode 100644 index 00000000..07689a10 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/spawn_multiple_times.c b/core/iwasm/libraries/lib-wasi-threads/test/spawn_multiple_times.c new file mode 100644 index 00000000..24664c47 --- /dev/null +++ b/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 +#include +#include +#include + +#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; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c b/core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c new file mode 100644 index 00000000..b8cf5649 --- /dev/null +++ b/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 +#include +#include +#include + +#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; +} diff --git a/samples/wasi-threads/wasm-apps/no_pthread.c b/samples/wasi-threads/wasm-apps/no_pthread.c index fcf64a7b..dc3c9553 100644 --- a/samples/wasi-threads/wasm-apps/no_pthread.c +++ b/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); } diff --git a/samples/wasi-threads/wasm-apps/thread_termination.c b/samples/wasi-threads/wasm-apps/thread_termination.c index 355bb3f4..9f5cf1fe 100644 --- a/samples/wasi-threads/wasm-apps/thread_termination.c +++ b/samples/wasi-threads/wasm-apps/thread_termination.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include @@ -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; } diff --git a/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh b/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh index cd3fe187..17c28243 100755 --- a/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh +++ b/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