forked from bytecodealliance/wasm-micro-runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add internal tests for WASI threads (bytecodealliance#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.
Showing
36 changed files
with
864 additions
and
11 deletions.
There are no files selected for viewing
This file contains 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 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 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 | ||
|
||
# | ||
# 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 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 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.