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