Skip to content

Commit

Permalink
Add comments to threading example and README
Browse files Browse the repository at this point in the history
  • Loading branch information
keithw committed Nov 19, 2023
1 parent 4cc0763 commit d2c5c41
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 33 deletions.
7 changes: 7 additions & 0 deletions wasm2c/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*, uint32_t eleme
void wasm_rt_free_funcref_table(wasm_rt_table_t*);
void wasm_rt_free_externref_table(wasm_rt_table_t*);
uint32_t wasm_rt_call_stack_depth; /* on platforms that don't use the signal handler to detect exhaustion */
void* wasm_rt_init_thread(void);
void wasm_rt_free_thread(void*);
```
`wasm_rt_init` must be called by the embedder before anything else, to
Expand Down Expand Up @@ -339,6 +341,11 @@ shared between modules, it must be defined only once, by the embedder.
It is only used on platforms that don't use the signal handler to detect
exhaustion.
`wasm_rt_init_thread` and `wasm_rt_free_thread` are used to initialize
and free the runtime state for a given thread (other than the one that
called `wasm_rt_init`). An example can be found in
`wasm2c/examples/threads`.
### Runtime support for exception handling
Several additional symbols must be defined if wasm2c is being run with support
Expand Down
91 changes: 60 additions & 31 deletions wasm2c/examples/threads/threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,17 @@

#define NUM_THREADS 1024

void* do_thread(void* arg) {
int param;
memcpy(&param, arg, sizeof(int));

void* context = wasm_rt_init_thread();

w2c_sample inst;
wasm2c_sample_instantiate(&inst);

wasm_rt_trap_t code = wasm_rt_impl_try();
if (code != 0) {
if (code == WASM_RT_TRAP_OOB || code == WASM_RT_TRAP_EXHAUSTION) {
int returnval = w2c_sample_multiplyby3(&inst, param);
memcpy(arg, &returnval, sizeof(int));

wasm2c_sample_free(&inst);
wasm_rt_free_thread(context);

return arg;
} else {
fprintf(stderr, "Expected OOB or exhaustion trap but got %s\n",
wasm_rt_strerror(code));
return NULL;
}
}

w2c_sample_stackoverflow(&inst);

fprintf(stderr, "Expected trap but did not get one\n");
return NULL;
}
void* do_thread(void* arg);

/**
* Example demonstrating use of the wasm2c runtime in multithreaded code.
*
* The program calls wasm_rt_init() on startup, and each new thread calls
* wasm_rt_init_thread() before instantiating a Wasm module. The sample
* module is designed to trap with stack exhaustion; this example tests
* that each thread can successfully catch the trap (in its own altstack)
* independently.
*/

int main(int argc, char** argv) {
pthread_t threads[NUM_THREADS];
Expand All @@ -46,6 +26,7 @@ int main(int argc, char** argv) {
/* Initialize the Wasm runtime. */
wasm_rt_init();

/* Create and launch threads running the `do_thread` function. */
for (int i = 0; i < NUM_THREADS; ++i) {
arguments[i] = i;
if (pthread_create(&threads[i], NULL, do_thread, &arguments[i])) {
Expand All @@ -54,22 +35,70 @@ int main(int argc, char** argv) {
}
}

/* Join each thread. */
for (int i = 0; i < NUM_THREADS; ++i) {
void* retval;
if (pthread_join(threads[i], &retval)) {
perror("pthread_join");
exit(EXIT_FAILURE);
}

/* Verify returned value is as expected */
if ((retval != &arguments[i]) || (arguments[i] != 3 * i)) {
fprintf(stderr, "Unexpected return value from thread.\n");
exit(EXIT_FAILURE);
}
}

/* Free the Wasm runtime's state. */
wasm_rt_free();

printf("%d/%d threads trapped successfully.\n", NUM_THREADS, NUM_THREADS);

return EXIT_SUCCESS;
}

void* do_thread(void* arg) {
int param;
memcpy(&param, arg, sizeof(int));

/* Initialize the per-thread context for the Wasm runtime (in practice, an
altstack for catching segfaults caused by stack exhaustion or out-of-bounds
memory access. */
void* context = wasm_rt_init_thread();

/* Instantiate the Wasm module. */
w2c_sample inst;
wasm2c_sample_instantiate(&inst);

/* Expect a stack-exhaustion trap. (N.B. in a pthreads-created stack, Linux's
segfault for stack overflow appears the same as one for memory OOB. This is
similar to the behavior of macOS when exhausting a non-pthreads stack. */
wasm_rt_trap_t code = wasm_rt_impl_try();
if (code != 0) {
if (code == WASM_RT_TRAP_OOB || code == WASM_RT_TRAP_EXHAUSTION) {
/* Trap arrived as expected. Now call the "real" function. */
int returnval = w2c_sample_multiplyby3(&inst, param);
memcpy(arg, &returnval, sizeof(int));

/* Free the module instance. */
wasm2c_sample_free(&inst);

/* Free the per-thread runtime context. */
wasm_rt_free_thread(context);

return arg;
} else {
fprintf(stderr, "Expected OOB or exhaustion trap but got %s\n",
wasm_rt_strerror(code));
return NULL;
}
}

/* Call the stack-overflow function. Expect a trap back to above. */
w2c_sample_stackoverflow(&inst);

/* If no trap... */
fprintf(stderr, "Expected trap but did not get one\n");
return NULL;
}
5 changes: 3 additions & 2 deletions wasm2c/wasm-rt.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,9 @@ void wasm_rt_free(void);

/*
* Initialize the multithreaded runtime for a given thread. Must be
* called by each thread before initializing a Wasm module or calling an
* exported function. Returns an opaque state pointer.
* called by each thread (other than the one that called wasm_rt_init)
* before initializing a Wasm module or calling an exported
* function. Returns an opaque state pointer.
*/
void* wasm_rt_init_thread(void);

Expand Down

0 comments on commit d2c5c41

Please sign in to comment.