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>
get_thread_id()32 uint64_t get_thread_id() { return (uint64_t)syscall(SYS_gettid); }
33
34 #elif defined(__APPLE__)
35 #include <pthread.h>
get_thread_id()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.
callback(const wasm_val_vec_t * args,wasm_val_vec_t * results)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
run(void * args_abs)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
main()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
exit_with_error(const char * message,wasmtime_error_t * error,wasm_trap_t * trap)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
main(int argc,const char * argv[])209 int main(int argc, const char *argv[]) { return 0; }
210 #endif // _WIN32
211