1 /* 2 Example of instantiating of the WebAssembly module and invoking its exported 3 function in a separate thread. 4 5 You can build using cmake: 6 7 mkdir build && cd build && cmake .. && cmake --build . --target wasmtime-threads 8 */ 9 10 #ifndef _WIN32 11 12 #include <inttypes.h> 13 #include <pthread.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <unistd.h> 18 #include <wasm.h> 19 #include <wasmtime.h> 20 21 #define own 22 23 static void exit_with_error(const char *message, wasmtime_error_t *error, 24 wasm_trap_t *trap); 25 26 const int N_THREADS = 10; 27 const int N_REPS = 3; 28 29 #if defined(__linux__) 30 #define _GNU_SOURCE 31 #include <sys/syscall.h> 32 uint64_t get_thread_id() { return (uint64_t)syscall(SYS_gettid); } 33 34 #elif defined(__APPLE__) 35 #include <pthread.h> 36 uint64_t get_thread_id() { 37 uint64_t tid; 38 pthread_threadid_np(NULL, &tid); 39 return tid; 40 } 41 42 #endif 43 44 // A function to be called from Wasm code. 45 own wasm_trap_t *callback(const wasm_val_vec_t *args, wasm_val_vec_t *results) { 46 printf("> Thread %lu running\n", (uint64_t)get_thread_id()); 47 return NULL; 48 } 49 50 typedef struct { 51 wasm_engine_t *engine; 52 wasm_shared_module_t *module; 53 int id; 54 } thread_args; 55 56 void *run(void *args_abs) { 57 thread_args *args = (thread_args *)args_abs; 58 59 // Rereate store and module. 60 own wasm_store_t *store = wasm_store_new(args->engine); 61 own wasm_module_t *module = wasm_module_obtain(store, args->module); 62 63 // Run the example N times. 64 for (int i = 0; i < N_REPS; ++i) { 65 usleep(100000); 66 67 // Create imports. 68 own wasm_functype_t *func_type = wasm_functype_new_0_0(); 69 own wasm_func_t *func = wasm_func_new(store, func_type, callback); 70 wasm_functype_delete(func_type); 71 72 // Instantiate. 73 wasm_extern_t *imports[] = { 74 wasm_func_as_extern(func), 75 }; 76 wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports); 77 own wasm_instance_t *instance = 78 wasm_instance_new(store, module, &imports_vec, NULL); 79 if (!instance) { 80 printf("> Error instantiating module!\n"); 81 return NULL; 82 } 83 84 wasm_func_delete(func); 85 86 // Extract export. 87 own wasm_extern_vec_t exports; 88 wasm_instance_exports(instance, &exports); 89 if (exports.size == 0) { 90 printf("> Error accessing exports!\n"); 91 return NULL; 92 } 93 const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]); 94 if (run_func == NULL) { 95 printf("> Error accessing export!\n"); 96 return NULL; 97 } 98 99 wasm_instance_delete(instance); 100 101 // Call. 102 wasm_val_vec_t args_vec = WASM_EMPTY_VEC; 103 wasm_val_vec_t results_vec = WASM_EMPTY_VEC; 104 if (wasm_func_call(run_func, &args_vec, &results_vec)) { 105 printf("> Error calling function!\n"); 106 return NULL; 107 } 108 109 wasm_extern_vec_delete(&exports); 110 } 111 112 wasm_module_delete(module); 113 wasm_store_delete(store); 114 115 free(args_abs); 116 117 return NULL; 118 } 119 120 int main(int argc, const char *argv[]) { 121 // Initialize. 122 wasm_engine_t *engine = wasm_engine_new(); 123 124 // Load our input file to parse it next 125 FILE *file = fopen("examples/threads.wat", "r"); 126 if (!file) { 127 printf("> Error loading file!\n"); 128 return 1; 129 } 130 fseek(file, 0L, SEEK_END); 131 size_t file_size = ftell(file); 132 fseek(file, 0L, SEEK_SET); 133 wasm_byte_vec_t wat; 134 wasm_byte_vec_new_uninitialized(&wat, file_size); 135 if (fread(wat.data, file_size, 1, file) != 1) { 136 printf("> Error loading module!\n"); 137 return 1; 138 } 139 fclose(file); 140 141 // Parse the wat into the binary wasm format 142 wasm_byte_vec_t binary; 143 wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary); 144 if (error != NULL) 145 exit_with_error("failed to parse wat", error, NULL); 146 wasm_byte_vec_delete(&wat); 147 148 // Compile and share. 149 own wasm_store_t *store = wasm_store_new(engine); 150 own wasm_module_t *module = wasm_module_new(store, &binary); 151 if (!module) { 152 printf("> Error compiling module!\n"); 153 return 1; 154 } 155 156 wasm_byte_vec_delete(&binary); 157 158 own wasm_shared_module_t *shared = wasm_module_share(module); 159 160 wasm_module_delete(module); 161 wasm_store_delete(store); 162 163 // Spawn threads. 164 pthread_t threads[N_THREADS]; 165 for (int i = 0; i < N_THREADS; i++) { 166 thread_args *args = malloc(sizeof(thread_args)); 167 args->engine = engine; 168 args->module = shared; 169 printf("Initializing thread %d...\n", i); 170 171 // Guarantee at least 2MB of stack to allow running Cranelift in debug mode 172 // on CI. 173 pthread_attr_t attrs; 174 pthread_attr_init(&attrs); 175 pthread_attr_setstacksize(&attrs, 2 << 20); 176 pthread_create(&threads[i], &attrs, &run, args); 177 pthread_attr_destroy(&attrs); 178 } 179 180 for (int i = 0; i < N_THREADS; i++) { 181 printf("Waiting for thread: %d\n", i); 182 pthread_join(threads[i], NULL); 183 } 184 185 wasm_shared_module_delete(shared); 186 wasm_engine_delete(engine); 187 188 return 0; 189 } 190 191 static void exit_with_error(const char *message, wasmtime_error_t *error, 192 wasm_trap_t *trap) { 193 fprintf(stderr, "error: %s\n", message); 194 wasm_byte_vec_t error_message; 195 if (error != NULL) { 196 wasmtime_error_message(error, &error_message); 197 } else { 198 wasm_trap_message(trap, &error_message); 199 } 200 fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data); 201 wasm_byte_vec_delete(&error_message); 202 exit(1); 203 } 204 205 #else 206 // TODO implement example for Windows 207 int main(int argc, const char *argv[]) { return 0; } 208 #endif // _WIN32 209