xref: /wasmtime-44.0.1/examples/threads.c (revision 10d2cbc5)
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