-
Notifications
You must be signed in to change notification settings - Fork 750
Add internal tests for WASI threads #1963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
wenyongh
merged 7 commits into
bytecodealliance:main
from
eloparco:eloparco/test-wasi-threads
Mar 9, 2023
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
12b2d84
test(wasi-threads): add tests for wasi threads
eloparco 4b2ae05
feat(wasi-threads): add test execution in ci
eloparco f8121f8
feat: add tests for thread termination
eloparco 1ccc5c5
fix: remove prints from different threads causing data races
eloparco e6ddf59
chore: move wasi thread test folder
eloparco 3dc9a6f
fix: use barrier instead of semaphore in thread termination tests
eloparco 7aafc8d
chore: add copyright
eloparco File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #!/bin/bash | ||
eloparco marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # | ||
| # 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 changes: 128 additions & 0 deletions
128
core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we upgrade to wasi-sdk 20, so we don't need to build libc from git?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I'm addressing that in a separate PR
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think I should update it everywhere in the CI or only for the parts where WASI threads are used?
eloparco@4b8c49e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had better update here only since we claimed in the document that we support the wasi-sdk 19.0+, we had better use it by default in the CI files.
https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/build_wasm_app.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried it here eloparco@006534c. It works on MacOS but not on Ubuntu 20.04, as we observed in the past (because of the glibc version installed there).
https://github.com/eloparco/wasm-micro-runtime/actions/runs/4379797818/jobs/7666263974
https://github.com/eloparco/wasm-micro-runtime/actions/runs/4379796629/jobs/7666151109
We can use wasi-sdk-20 pre-release in case of Ubuntu 22.04 and keep using wasi-libc for wasi-sdk-19 on Ubuntu 20.04, making sure that in case of wasi-sdk-19 we specify
WASI_SYSROOT. What do you think?Maybe I should create a PR anyway and we can discuss there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's good, at least we don't need to build wasi-libc in Ubuntu 22.04. And we can update CI again when the wasi-sdk-20 is actually released. Not sure whether the glibc version issue on Ubuntu 20.04 will be resolved in wasi-sdk-20.0? Or should we file an issue in wasi-sdk community, or upload a PR to help resolve the issue?