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