Skip to content

Commit

Permalink
test(wasi-threads): add tests for wasi threads
Browse files Browse the repository at this point in the history
  • Loading branch information
eloparco committed Feb 18, 2023
1 parent 739acfc commit 99b8f88
Showing 8 changed files with 469 additions and 1 deletion.
25 changes: 25 additions & 0 deletions core/iwasm/libraries/wasi-threads/test/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

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
128 changes: 128 additions & 0 deletions core/iwasm/libraries/wasi-threads/test/create_threads_until_limit.c
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;
}
69 changes: 69 additions & 0 deletions core/iwasm/libraries/wasi-threads/test/global_atomic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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 "wasi_thread_start.h"

enum CONSTANTS {
NUM_THREADS = 5,
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;
}
85 changes: 85 additions & 0 deletions core/iwasm/libraries/wasi-threads/test/global_lock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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];

if (pthread_mutex_init(&mutex, NULL) != 0) {
printf("Failed to init mutex.\n");
return EXIT_FAILURE;
}

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");

if (pthread_mutex_destroy(&mutex) != 0) {
printf("Failed to init mutex.\n");
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}
75 changes: 75 additions & 0 deletions core/iwasm/libraries/wasi-threads/test/spawn_multiple_times.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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 };
if (!start_args_init(&data.base)) {
printf("Stack allocation for thread failed\n");
return EXIT_FAILURE;
}

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;
}
Loading

0 comments on commit 99b8f88

Please sign in to comment.