12ba3025eSTheGreatRambler /*
22ba3025eSTheGreatRambler Example of instantiating of the WebAssembly module and invoking its exported
32ba3025eSTheGreatRambler function in a separate thread.
42ba3025eSTheGreatRambler
557ba95e9SMasashi Yoshimura You can build using cmake:
62ba3025eSTheGreatRambler
72ba3025eSTheGreatRambler mkdir build && cd build && cmake .. && cmake --build . --target wasmtime-threads
82ba3025eSTheGreatRambler */
92ba3025eSTheGreatRambler
1015c68f2cSYury Delendik #ifndef _WIN32
1115c68f2cSYury Delendik
1215c68f2cSYury Delendik #include <inttypes.h>
13f8fee938STyler Rockwood #include <pthread.h>
1415c68f2cSYury Delendik #include <stdio.h>
1515c68f2cSYury Delendik #include <stdlib.h>
1615c68f2cSYury Delendik #include <string.h>
1715c68f2cSYury Delendik #include <unistd.h>
1815c68f2cSYury Delendik #include <wasm.h>
1915c68f2cSYury Delendik #include <wasmtime.h>
2015c68f2cSYury Delendik
2115c68f2cSYury Delendik #define own
2215c68f2cSYury Delendik
23f8fee938STyler Rockwood static void exit_with_error(const char *message, wasmtime_error_t *error,
24f8fee938STyler Rockwood wasm_trap_t *trap);
2515c68f2cSYury Delendik
2615c68f2cSYury Delendik const int N_THREADS = 10;
2715c68f2cSYury Delendik const int N_REPS = 3;
2815c68f2cSYury Delendik
2957ba95e9SMasashi Yoshimura #if defined(__linux__)
3057ba95e9SMasashi Yoshimura #define _GNU_SOURCE
3157ba95e9SMasashi Yoshimura #include <sys/syscall.h>
get_thread_id()3257ba95e9SMasashi Yoshimura uint64_t get_thread_id() { return (uint64_t)syscall(SYS_gettid); }
3357ba95e9SMasashi Yoshimura
3457ba95e9SMasashi Yoshimura #elif defined(__APPLE__)
3557ba95e9SMasashi Yoshimura #include <pthread.h>
get_thread_id()3657ba95e9SMasashi Yoshimura uint64_t get_thread_id() {
3757ba95e9SMasashi Yoshimura uint64_t tid;
3857ba95e9SMasashi Yoshimura pthread_threadid_np(NULL, &tid);
3957ba95e9SMasashi Yoshimura return tid;
4057ba95e9SMasashi Yoshimura }
4157ba95e9SMasashi Yoshimura
4257ba95e9SMasashi Yoshimura #endif
4357ba95e9SMasashi Yoshimura
4415c68f2cSYury Delendik // A function to be called from Wasm code.
callback(const wasm_val_vec_t * args,wasm_val_vec_t * results)45f94db655SPeter Huene own wasm_trap_t *callback(const wasm_val_vec_t *args, wasm_val_vec_t *results) {
46*adff9d9dSAlex Crichton (void)args;
47*adff9d9dSAlex Crichton (void)results;
4857ba95e9SMasashi Yoshimura printf("> Thread %lu running\n", (uint64_t)get_thread_id());
4915c68f2cSYury Delendik return NULL;
5015c68f2cSYury Delendik }
5115c68f2cSYury Delendik
5215c68f2cSYury Delendik typedef struct {
5315c68f2cSYury Delendik wasm_engine_t *engine;
5415c68f2cSYury Delendik wasm_shared_module_t *module;
5515c68f2cSYury Delendik int id;
5615c68f2cSYury Delendik } thread_args;
5715c68f2cSYury Delendik
run(void * args_abs)5815c68f2cSYury Delendik void *run(void *args_abs) {
5915c68f2cSYury Delendik thread_args *args = (thread_args *)args_abs;
6015c68f2cSYury Delendik
6115c68f2cSYury Delendik // Rereate store and module.
6215c68f2cSYury Delendik own wasm_store_t *store = wasm_store_new(args->engine);
6315c68f2cSYury Delendik own wasm_module_t *module = wasm_module_obtain(store, args->module);
6415c68f2cSYury Delendik
6515c68f2cSYury Delendik // Run the example N times.
6615c68f2cSYury Delendik for (int i = 0; i < N_REPS; ++i) {
6715c68f2cSYury Delendik usleep(100000);
6815c68f2cSYury Delendik
6915c68f2cSYury Delendik // Create imports.
7057ba95e9SMasashi Yoshimura own wasm_functype_t *func_type = wasm_functype_new_0_0();
7115c68f2cSYury Delendik own wasm_func_t *func = wasm_func_new(store, func_type, callback);
7215c68f2cSYury Delendik wasm_functype_delete(func_type);
7315c68f2cSYury Delendik
7415c68f2cSYury Delendik // Instantiate.
75f94db655SPeter Huene wasm_extern_t *imports[] = {
76f8fee938STyler Rockwood wasm_func_as_extern(func),
7715c68f2cSYury Delendik };
78f94db655SPeter Huene wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports);
7915c68f2cSYury Delendik own wasm_instance_t *instance =
80f94db655SPeter Huene wasm_instance_new(store, module, &imports_vec, NULL);
8115c68f2cSYury Delendik if (!instance) {
8215c68f2cSYury Delendik printf("> Error instantiating module!\n");
8315c68f2cSYury Delendik return NULL;
8415c68f2cSYury Delendik }
8515c68f2cSYury Delendik
8615c68f2cSYury Delendik wasm_func_delete(func);
8715c68f2cSYury Delendik
8815c68f2cSYury Delendik // Extract export.
8915c68f2cSYury Delendik own wasm_extern_vec_t exports;
9015c68f2cSYury Delendik wasm_instance_exports(instance, &exports);
9115c68f2cSYury Delendik if (exports.size == 0) {
9215c68f2cSYury Delendik printf("> Error accessing exports!\n");
9315c68f2cSYury Delendik return NULL;
9415c68f2cSYury Delendik }
9515c68f2cSYury Delendik const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]);
9615c68f2cSYury Delendik if (run_func == NULL) {
9715c68f2cSYury Delendik printf("> Error accessing export!\n");
9815c68f2cSYury Delendik return NULL;
9915c68f2cSYury Delendik }
10015c68f2cSYury Delendik
10115c68f2cSYury Delendik wasm_instance_delete(instance);
10215c68f2cSYury Delendik
10315c68f2cSYury Delendik // Call.
104f94db655SPeter Huene wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
105f94db655SPeter Huene wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
106f94db655SPeter Huene if (wasm_func_call(run_func, &args_vec, &results_vec)) {
10715c68f2cSYury Delendik printf("> Error calling function!\n");
10815c68f2cSYury Delendik return NULL;
10915c68f2cSYury Delendik }
11015c68f2cSYury Delendik
11115c68f2cSYury Delendik wasm_extern_vec_delete(&exports);
11215c68f2cSYury Delendik }
11315c68f2cSYury Delendik
11415c68f2cSYury Delendik wasm_module_delete(module);
11515c68f2cSYury Delendik wasm_store_delete(store);
11615c68f2cSYury Delendik
11715c68f2cSYury Delendik free(args_abs);
11815c68f2cSYury Delendik
11915c68f2cSYury Delendik return NULL;
12015c68f2cSYury Delendik }
12115c68f2cSYury Delendik
main()122*adff9d9dSAlex Crichton int main() {
12315c68f2cSYury Delendik // Initialize.
12415c68f2cSYury Delendik wasm_engine_t *engine = wasm_engine_new();
12515c68f2cSYury Delendik
12615c68f2cSYury Delendik // Load our input file to parse it next
12715c68f2cSYury Delendik FILE *file = fopen("examples/threads.wat", "r");
12815c68f2cSYury Delendik if (!file) {
12915c68f2cSYury Delendik printf("> Error loading file!\n");
13015c68f2cSYury Delendik return 1;
13115c68f2cSYury Delendik }
13215c68f2cSYury Delendik fseek(file, 0L, SEEK_END);
13315c68f2cSYury Delendik size_t file_size = ftell(file);
13415c68f2cSYury Delendik fseek(file, 0L, SEEK_SET);
13515c68f2cSYury Delendik wasm_byte_vec_t wat;
13615c68f2cSYury Delendik wasm_byte_vec_new_uninitialized(&wat, file_size);
13715c68f2cSYury Delendik if (fread(wat.data, file_size, 1, file) != 1) {
13815c68f2cSYury Delendik printf("> Error loading module!\n");
13915c68f2cSYury Delendik return 1;
14015c68f2cSYury Delendik }
14115c68f2cSYury Delendik fclose(file);
14215c68f2cSYury Delendik
14315c68f2cSYury Delendik // Parse the wat into the binary wasm format
14415c68f2cSYury Delendik wasm_byte_vec_t binary;
1457a1b7cdfSAlex Crichton wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary);
14615c68f2cSYury Delendik if (error != NULL)
14715c68f2cSYury Delendik exit_with_error("failed to parse wat", error, NULL);
14815c68f2cSYury Delendik wasm_byte_vec_delete(&wat);
14915c68f2cSYury Delendik
15015c68f2cSYury Delendik // Compile and share.
15115c68f2cSYury Delendik own wasm_store_t *store = wasm_store_new(engine);
15215c68f2cSYury Delendik own wasm_module_t *module = wasm_module_new(store, &binary);
15315c68f2cSYury Delendik if (!module) {
15415c68f2cSYury Delendik printf("> Error compiling module!\n");
15515c68f2cSYury Delendik return 1;
15615c68f2cSYury Delendik }
15715c68f2cSYury Delendik
15815c68f2cSYury Delendik wasm_byte_vec_delete(&binary);
15915c68f2cSYury Delendik
16015c68f2cSYury Delendik own wasm_shared_module_t *shared = wasm_module_share(module);
16115c68f2cSYury Delendik
16215c68f2cSYury Delendik wasm_module_delete(module);
16315c68f2cSYury Delendik wasm_store_delete(store);
16415c68f2cSYury Delendik
16515c68f2cSYury Delendik // Spawn threads.
16615c68f2cSYury Delendik pthread_t threads[N_THREADS];
16715c68f2cSYury Delendik for (int i = 0; i < N_THREADS; i++) {
16815c68f2cSYury Delendik thread_args *args = malloc(sizeof(thread_args));
16915c68f2cSYury Delendik args->engine = engine;
17015c68f2cSYury Delendik args->module = shared;
17115c68f2cSYury Delendik printf("Initializing thread %d...\n", i);
17221c065e3SAlex Crichton
17321c065e3SAlex Crichton // Guarantee at least 2MB of stack to allow running Cranelift in debug mode
17421c065e3SAlex Crichton // on CI.
17521c065e3SAlex Crichton pthread_attr_t attrs;
17621c065e3SAlex Crichton pthread_attr_init(&attrs);
17721c065e3SAlex Crichton pthread_attr_setstacksize(&attrs, 2 << 20);
17821c065e3SAlex Crichton pthread_create(&threads[i], &attrs, &run, args);
17921c065e3SAlex Crichton pthread_attr_destroy(&attrs);
18015c68f2cSYury Delendik }
18115c68f2cSYury Delendik
18215c68f2cSYury Delendik for (int i = 0; i < N_THREADS; i++) {
18315c68f2cSYury Delendik printf("Waiting for thread: %d\n", i);
18415c68f2cSYury Delendik pthread_join(threads[i], NULL);
18515c68f2cSYury Delendik }
18615c68f2cSYury Delendik
18715c68f2cSYury Delendik wasm_shared_module_delete(shared);
18815c68f2cSYury Delendik wasm_engine_delete(engine);
18915c68f2cSYury Delendik
19015c68f2cSYury Delendik return 0;
19115c68f2cSYury Delendik }
19215c68f2cSYury Delendik
exit_with_error(const char * message,wasmtime_error_t * error,wasm_trap_t * trap)193f8fee938STyler Rockwood static void exit_with_error(const char *message, wasmtime_error_t *error,
194f8fee938STyler Rockwood wasm_trap_t *trap) {
19515c68f2cSYury Delendik fprintf(stderr, "error: %s\n", message);
19615c68f2cSYury Delendik wasm_byte_vec_t error_message;
19715c68f2cSYury Delendik if (error != NULL) {
19815c68f2cSYury Delendik wasmtime_error_message(error, &error_message);
19915c68f2cSYury Delendik } else {
20015c68f2cSYury Delendik wasm_trap_message(trap, &error_message);
20115c68f2cSYury Delendik }
20215c68f2cSYury Delendik fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data);
20315c68f2cSYury Delendik wasm_byte_vec_delete(&error_message);
20415c68f2cSYury Delendik exit(1);
20515c68f2cSYury Delendik }
20615c68f2cSYury Delendik
20715c68f2cSYury Delendik #else
20815c68f2cSYury Delendik // TODO implement example for Windows
main(int argc,const char * argv[])209f8fee938STyler Rockwood int main(int argc, const char *argv[]) { return 0; }
21015c68f2cSYury Delendik #endif // _WIN32
211