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