Skip to content

Commit

Permalink
Fix examples so they correctly set the stack pointer on thread startup
Browse files Browse the repository at this point in the history
For pthreads this is configured in wasi-libc, but since we don't use
pthreads here, we have to do it explicitly.

The wasi_thread_start code is implemented in wasm to make sure there's no
stack operations before the stack pointer is set to a correct value.
  • Loading branch information
loganek committed Jan 6, 2023
1 parent 4ec4744 commit dfa58f8
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 20 deletions.
7 changes: 4 additions & 3 deletions samples/wasi-threads/wasm-apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ endif ()

set (CMAKE_SYSROOT "${WASI_SYSROOT}")
set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang")
set (CMAKE_ASM_COMPILER "${WASI_SDK_DIR}/bin/clang")
set (CMAKE_C_COMPILER_TARGET "wasm32-wasi")

function (compile_sample SOURCE_FILE)
get_filename_component (FILE_NAME ${SOURCE_FILE} NAME_WLE)
set (WASM_MODULE ${FILE_NAME}.wasm)
add_executable (${WASM_MODULE} ${SOURCE_FILE})
add_executable (${WASM_MODULE} ${SOURCE_FILE} ${ARGN})

target_compile_options (${WASM_MODULE} PRIVATE
-pthread -ftls-model=local-exec)
Expand All @@ -34,5 +35,5 @@ function (compile_sample SOURCE_FILE)
)
endfunction ()

compile_sample(no_pthread.c)
compile_sample(exception_propagation.c)
compile_sample(no_pthread.c wasi_thread_start.S)
compile_sample(exception_propagation.c wasi_thread_start.S)
36 changes: 26 additions & 10 deletions samples/wasi-threads/wasm-apps/exception_propagation.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <wasi/api.h>
#include <semaphore.h>
#include <stdbool.h>
#include <unistd.h>

#include "wasi_thread_start.h"

#define TIMEOUT_SECONDS 10
#define NUM_THREADS 3
static sem_t sem;

typedef struct {
start_args_t base;
bool throw_exception;
} shared_t;

void
run_long_task()
{
Expand All @@ -26,12 +32,12 @@ run_long_task()
sleep(1);
}

__attribute__((export_name("wasi_thread_start"))) void
wasi_thread_start(int thread_id, int *start_arg)
void
__wasi_thread_start_C(int thread_id, int *start_arg)
{
bool has_to_throw_exception = (bool)start_arg;
shared_t *data = (shared_t *)start_arg;

if (has_to_throw_exception) {
if (data->throw_exception) {
// Wait for all other threads (including main thread) to be ready
printf("Waiting before throwing exception\n");
for (int i = 0; i < NUM_THREADS; i++)
Expand All @@ -52,26 +58,36 @@ wasi_thread_start(int thread_id, int *start_arg)
int
main(int argc, char **argv)
{
int thread_id = -1;
int thread_id = -1, i;
shared_t data[NUM_THREADS] = { 0 };

if (sem_init(&sem, 0, 0) != 0) {
printf("Failed to init semaphore\n");
return EXIT_FAILURE;
}

for (i = 0; i < NUM_THREADS; i++) {
// No graceful memory free to simplify the example
if (!start_args_init(&data[i].base)) {
printf("Failed to allocate thread's stack\n");
return EXIT_FAILURE;
}
}

// Create a thread that throws an exception
thread_id = __wasi_thread_spawn((void *)true);
data[0].throw_exception = true;
thread_id = __wasi_thread_spawn(&data[0]);
if (thread_id < 0) {
printf("Failed to create thread: %d\n", thread_id);
return EXIT_FAILURE;
}

// Create two additional threads to test exception propagation
thread_id = __wasi_thread_spawn((void *)false);
thread_id = __wasi_thread_spawn(&data[1]);
if (thread_id < 0) {
printf("Failed to create thread: %d\n", thread_id);
return EXIT_FAILURE;
}
thread_id = __wasi_thread_spawn((void *)false);
thread_id = __wasi_thread_spawn(&data[2]);
if (thread_id < 0) {
printf("Failed to create thread: %d\n", thread_id);
return EXIT_FAILURE;
Expand Down
27 changes: 20 additions & 7 deletions samples/wasi-threads/wasm-apps/no_pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <wasi/api.h>

#include "wasi_thread_start.h"

static const int64_t SECOND = 1000 * 1000 * 1000;

typedef struct {
start_args_t base;
int th_ready;
int value;
int thread_id;
} shared_t;

__attribute__((export_name("wasi_thread_start"))) void
wasi_thread_start(int thread_id, int *start_arg)
void
__wasi_thread_start_C(int thread_id, int *start_arg)
{
shared_t *data = (shared_t *)start_arg;

Expand All @@ -38,24 +40,35 @@ wasi_thread_start(int thread_id, int *start_arg)
int
main(int argc, char **argv)
{
shared_t data = { 0, 52, -1 };
shared_t data = { { NULL }, 0, 52, -1 };
int thread_id;
int ret = EXIT_SUCCESS;

if (!start_args_init(&data.base)) {
printf("Stack allocation for thread failed\n");
return EXIT_FAILURE;
}

thread_id = __wasi_thread_spawn(&data);
if (thread_id < 0) {
printf("Failed to create thread: %d\n", thread_id);
return EXIT_FAILURE;
ret = EXIT_FAILURE;
goto final;
}

if (__builtin_wasm_memory_atomic_wait32(&data.th_ready, 0, SECOND) == 2) {
printf("Timeout\n");
return EXIT_FAILURE;
ret = EXIT_FAILURE;
goto final;
}

printf("Thread completed, new value: %d, thread id: %d\n", data.value,
data.thread_id);

assert(thread_id == data.thread_id);

return EXIT_SUCCESS;
final:
start_args_deinit(&data.base);

return ret;
}
20 changes: 20 additions & 0 deletions samples/wasi-threads/wasm-apps/wasi_thread_start.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.globaltype __stack_pointer, i32
.functype __wasi_thread_start_C (i32, i32) -> ()

.globl wasi_thread_start

wasi_thread_start:
.functype wasi_thread_start (i32, i32) -> ()

# Set up the minimum C environment.
# Note: offsetof(start_arg, stack) == 0
local.get 1 # start_arg
i32.load 0 # stack
global.set __stack_pointer

# Make the C function do the rest of work.
local.get 0 # tid
local.get 1 # start_arg
call __wasi_thread_start_C

end_function

0 comments on commit dfa58f8

Please sign in to comment.