xref: /wasmtime-44.0.1/examples/threads.c (revision ae84e6ed)
1 /*
2 Example of instantiating of the WebAssembly module and invoking its exported
3 function in a separate thread.
4 
5 You can compile and run this example on Linux with:
6 
7    cargo build --release -p wasmtime-c-api
8    cc examples/threads.c \
9        -I crates/c-api/include \
10        target/release/libwasmtime.a \
11        -lpthread -ldl -lm \
12        -o threads
13    ./threads
14 
15 Note that on Windows and macOS the command will be similar, but you'll need
16 to tweak the `-lpthread` and such annotations as well as the name of the
17 `libwasmtime.a` file on Windows.
18 
19 You can also build using cmake:
20 
21 mkdir build && cd build && cmake .. && cmake --build . --target wasmtime-threads
22 */
23 
24 #ifndef _WIN32
25 
26 #include <inttypes.h>
27 #include <pthread.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <wasm.h>
33 #include <wasmtime.h>
34 
35 #define own
36 
37 static void exit_with_error(const char *message, wasmtime_error_t *error,
38                             wasm_trap_t *trap);
39 
40 const int N_THREADS = 10;
41 const int N_REPS = 3;
42 
43 // A function to be called from Wasm code.
44 own wasm_trap_t *callback(const wasm_val_vec_t *args, wasm_val_vec_t *results) {
45   assert(args->data[0].kind == WASM_I32);
46   printf("> Thread %d running\n", args->data[0].of.i32);
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 =
69         wasm_functype_new_1_0(wasm_valtype_new_i32());
70     own wasm_func_t *func = wasm_func_new(store, func_type, callback);
71     wasm_functype_delete(func_type);
72 
73     wasm_val_t val = {.kind = WASM_I32, .of = {.i32 = (int32_t)args->id}};
74     own wasm_globaltype_t *global_type =
75         wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST);
76     own wasm_global_t *global = wasm_global_new(store, global_type, &val);
77     wasm_globaltype_delete(global_type);
78 
79     // Instantiate.
80     wasm_extern_t *imports[] = {
81         wasm_func_as_extern(func),
82         wasm_global_as_extern(global),
83     };
84     wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports);
85     own wasm_instance_t *instance =
86         wasm_instance_new(store, module, &imports_vec, NULL);
87     if (!instance) {
88       printf("> Error instantiating module!\n");
89       return NULL;
90     }
91 
92     wasm_func_delete(func);
93     wasm_global_delete(global);
94 
95     // Extract export.
96     own wasm_extern_vec_t exports;
97     wasm_instance_exports(instance, &exports);
98     if (exports.size == 0) {
99       printf("> Error accessing exports!\n");
100       return NULL;
101     }
102     const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]);
103     if (run_func == NULL) {
104       printf("> Error accessing export!\n");
105       return NULL;
106     }
107 
108     wasm_instance_delete(instance);
109 
110     // Call.
111     wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
112     wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
113     if (wasm_func_call(run_func, &args_vec, &results_vec)) {
114       printf("> Error calling function!\n");
115       return NULL;
116     }
117 
118     wasm_extern_vec_delete(&exports);
119   }
120 
121   wasm_module_delete(module);
122   wasm_store_delete(store);
123 
124   free(args_abs);
125 
126   return NULL;
127 }
128 
129 int main(int argc, const char *argv[]) {
130   // Initialize.
131   wasm_engine_t *engine = wasm_engine_new();
132 
133   // Load our input file to parse it next
134   FILE *file = fopen("examples/threads.wat", "r");
135   if (!file) {
136     printf("> Error loading file!\n");
137     return 1;
138   }
139   fseek(file, 0L, SEEK_END);
140   size_t file_size = ftell(file);
141   fseek(file, 0L, SEEK_SET);
142   wasm_byte_vec_t wat;
143   wasm_byte_vec_new_uninitialized(&wat, file_size);
144   if (fread(wat.data, file_size, 1, file) != 1) {
145     printf("> Error loading module!\n");
146     return 1;
147   }
148   fclose(file);
149 
150   // Parse the wat into the binary wasm format
151   wasm_byte_vec_t binary;
152   wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary);
153   if (error != NULL)
154     exit_with_error("failed to parse wat", error, NULL);
155   wasm_byte_vec_delete(&wat);
156 
157   // Compile and share.
158   own wasm_store_t *store = wasm_store_new(engine);
159   own wasm_module_t *module = wasm_module_new(store, &binary);
160   if (!module) {
161     printf("> Error compiling module!\n");
162     return 1;
163   }
164 
165   wasm_byte_vec_delete(&binary);
166 
167   own wasm_shared_module_t *shared = wasm_module_share(module);
168 
169   wasm_module_delete(module);
170   wasm_store_delete(store);
171 
172   // Spawn threads.
173   pthread_t threads[N_THREADS];
174   for (int i = 0; i < N_THREADS; i++) {
175     thread_args *args = malloc(sizeof(thread_args));
176     args->id = i;
177     args->engine = engine;
178     args->module = shared;
179     printf("Initializing thread %d...\n", i);
180 
181     // Guarantee at least 2MB of stack to allow running Cranelift in debug mode
182     // on CI.
183     pthread_attr_t attrs;
184     pthread_attr_init(&attrs);
185     pthread_attr_setstacksize(&attrs, 2 << 20);
186     pthread_create(&threads[i], &attrs, &run, args);
187     pthread_attr_destroy(&attrs);
188   }
189 
190   for (int i = 0; i < N_THREADS; i++) {
191     printf("Waiting for thread: %d\n", i);
192     pthread_join(threads[i], NULL);
193   }
194 
195   wasm_shared_module_delete(shared);
196   wasm_engine_delete(engine);
197 
198   return 0;
199 }
200 
201 static void exit_with_error(const char *message, wasmtime_error_t *error,
202                             wasm_trap_t *trap) {
203   fprintf(stderr, "error: %s\n", message);
204   wasm_byte_vec_t error_message;
205   if (error != NULL) {
206     wasmtime_error_message(error, &error_message);
207   } else {
208     wasm_trap_message(trap, &error_message);
209   }
210   fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data);
211   wasm_byte_vec_delete(&error_message);
212   exit(1);
213 }
214 
215 #else
216 // TODO implement example for Windows
217 int main(int argc, const char *argv[]) { return 0; }
218 #endif // _WIN32
219