1*33224b22SAlex Crichton /*
2*33224b22SAlex Crichton Example of instantiating of the WebAssembly module and invoking its exported
3*33224b22SAlex Crichton function.
4*33224b22SAlex Crichton
5*33224b22SAlex Crichton You can compile and run this example on Linux with:
6*33224b22SAlex Crichton
7*33224b22SAlex Crichton cargo build --release -p wasmtime-c-api
8*33224b22SAlex Crichton c++ examples/async.cc \
9*33224b22SAlex Crichton -I crates/c-api/include \
10*33224b22SAlex Crichton target/release/libwasmtime.a \
11*33224b22SAlex Crichton -std=c++11 \
12*33224b22SAlex Crichton -lpthread -ldl -lm \
13*33224b22SAlex Crichton -o async
14*33224b22SAlex Crichton ./async
15*33224b22SAlex Crichton
16*33224b22SAlex Crichton Note that on Windows and macOS the command will be similar, but you'll need
17*33224b22SAlex Crichton to tweak the `-lpthread` and such annotations.
18*33224b22SAlex Crichton
19*33224b22SAlex Crichton You can also build using cmake:
20*33224b22SAlex Crichton
21*33224b22SAlex Crichton mkdir build && cd build && cmake .. && cmake --build . --target wasmtime-async
22*33224b22SAlex Crichton */
23*33224b22SAlex Crichton
24*33224b22SAlex Crichton #include <array>
25*33224b22SAlex Crichton #include <assert.h>
26*33224b22SAlex Crichton #include <chrono>
27*33224b22SAlex Crichton #include <cstdlib>
28*33224b22SAlex Crichton #include <fstream>
29*33224b22SAlex Crichton #include <future>
30*33224b22SAlex Crichton #include <iostream>
31*33224b22SAlex Crichton #include <memory>
32*33224b22SAlex Crichton #include <optional>
33*33224b22SAlex Crichton #include <sstream>
34*33224b22SAlex Crichton #include <streambuf>
35*33224b22SAlex Crichton #include <string>
36*33224b22SAlex Crichton #include <thread>
37*33224b22SAlex Crichton #include <wasmtime.h>
38*33224b22SAlex Crichton
39*33224b22SAlex Crichton namespace {
40*33224b22SAlex Crichton
41*33224b22SAlex Crichton template <typename T, void (*fn)(T *)> struct deleter {
operator ()__anonaedd7ee30111::deleter42*33224b22SAlex Crichton void operator()(T *ptr) { fn(ptr); }
43*33224b22SAlex Crichton };
44*33224b22SAlex Crichton template <typename T, void (*fn)(T *)>
45*33224b22SAlex Crichton using handle = std::unique_ptr<T, deleter<T, fn>>;
46*33224b22SAlex Crichton
exit_with_error(std::string msg,wasmtime_error_t * err,wasm_trap_t * trap)47*33224b22SAlex Crichton void exit_with_error(std::string msg, wasmtime_error_t *err,
48*33224b22SAlex Crichton wasm_trap_t *trap) {
49*33224b22SAlex Crichton std::cerr << "error: " << msg << std::endl;
50*33224b22SAlex Crichton wasm_byte_vec_t error_message;
51*33224b22SAlex Crichton if (err) {
52*33224b22SAlex Crichton wasmtime_error_message(err, &error_message);
53*33224b22SAlex Crichton } else {
54*33224b22SAlex Crichton wasm_trap_message(trap, &error_message);
55*33224b22SAlex Crichton }
56*33224b22SAlex Crichton std::cerr << std::string(error_message.data, error_message.size) << std::endl;
57*33224b22SAlex Crichton wasm_byte_vec_delete(&error_message);
58*33224b22SAlex Crichton std::exit(1);
59*33224b22SAlex Crichton }
60*33224b22SAlex Crichton
create_engine()61*33224b22SAlex Crichton handle<wasm_engine_t, wasm_engine_delete> create_engine() {
62*33224b22SAlex Crichton wasm_config_t *config = wasm_config_new();
63*33224b22SAlex Crichton assert(config != nullptr);
64*33224b22SAlex Crichton wasmtime_config_consume_fuel_set(config, true);
65*33224b22SAlex Crichton handle<wasm_engine_t, wasm_engine_delete> engine;
66*33224b22SAlex Crichton // this takes ownership of config
67*33224b22SAlex Crichton engine.reset(wasm_engine_new_with_config(config));
68*33224b22SAlex Crichton assert(engine);
69*33224b22SAlex Crichton return engine;
70*33224b22SAlex Crichton }
71*33224b22SAlex Crichton
72*33224b22SAlex Crichton handle<wasmtime_store_t, wasmtime_store_delete>
create_store(wasm_engine_t * engine)73*33224b22SAlex Crichton create_store(wasm_engine_t *engine) {
74*33224b22SAlex Crichton handle<wasmtime_store_t, wasmtime_store_delete> store;
75*33224b22SAlex Crichton store.reset(wasmtime_store_new(engine, nullptr, nullptr));
76*33224b22SAlex Crichton assert(store);
77*33224b22SAlex Crichton return store;
78*33224b22SAlex Crichton }
79*33224b22SAlex Crichton
80*33224b22SAlex Crichton handle<wasmtime_linker_t, wasmtime_linker_delete>
create_linker(wasm_engine_t * engine)81*33224b22SAlex Crichton create_linker(wasm_engine_t *engine) {
82*33224b22SAlex Crichton handle<wasmtime_linker_t, wasmtime_linker_delete> linker;
83*33224b22SAlex Crichton linker.reset(wasmtime_linker_new(engine));
84*33224b22SAlex Crichton assert(linker);
85*33224b22SAlex Crichton return linker;
86*33224b22SAlex Crichton }
87*33224b22SAlex Crichton
88*33224b22SAlex Crichton handle<wasmtime_module_t, wasmtime_module_delete>
compile_wat_module_from_file(wasm_engine_t * engine,const std::string & filename)89*33224b22SAlex Crichton compile_wat_module_from_file(wasm_engine_t *engine,
90*33224b22SAlex Crichton const std::string &filename) {
91*33224b22SAlex Crichton std::ifstream t(filename);
92*33224b22SAlex Crichton std::stringstream buffer;
93*33224b22SAlex Crichton buffer << t.rdbuf();
94*33224b22SAlex Crichton if (t.bad()) {
95*33224b22SAlex Crichton std::cerr << "error reading file: " << filename << std::endl;
96*33224b22SAlex Crichton std::exit(1);
97*33224b22SAlex Crichton }
98*33224b22SAlex Crichton const std::string &content = buffer.str();
99*33224b22SAlex Crichton wasm_byte_vec_t wasm_bytes;
100*33224b22SAlex Crichton handle<wasmtime_error_t, wasmtime_error_delete> error{
101*33224b22SAlex Crichton wasmtime_wat2wasm(content.data(), content.size(), &wasm_bytes)};
102*33224b22SAlex Crichton if (error) {
103*33224b22SAlex Crichton exit_with_error("failed to parse wat", error.get(), nullptr);
104*33224b22SAlex Crichton }
105*33224b22SAlex Crichton wasmtime_module_t *mod_ptr = nullptr;
106*33224b22SAlex Crichton error.reset(wasmtime_module_new(engine,
107*33224b22SAlex Crichton reinterpret_cast<uint8_t *>(wasm_bytes.data),
108*33224b22SAlex Crichton wasm_bytes.size, &mod_ptr));
109*33224b22SAlex Crichton wasm_byte_vec_delete(&wasm_bytes);
110*33224b22SAlex Crichton handle<wasmtime_module_t, wasmtime_module_delete> mod{mod_ptr};
111*33224b22SAlex Crichton if (!mod) {
112*33224b22SAlex Crichton exit_with_error("failed to compile module", error.get(), nullptr);
113*33224b22SAlex Crichton }
114*33224b22SAlex Crichton return mod;
115*33224b22SAlex Crichton }
116*33224b22SAlex Crichton
117*33224b22SAlex Crichton class printer_thread_state {
118*33224b22SAlex Crichton public:
set_value_to_print(int32_t v)119*33224b22SAlex Crichton void set_value_to_print(int32_t v) {
120*33224b22SAlex Crichton _print_finished_future = _print_finished.get_future();
121*33224b22SAlex Crichton _value_to_print.set_value(v);
122*33224b22SAlex Crichton }
get_value_to_print()123*33224b22SAlex Crichton int32_t get_value_to_print() { return _value_to_print.get_future().get(); }
124*33224b22SAlex Crichton
print_is_pending() const125*33224b22SAlex Crichton bool print_is_pending() const {
126*33224b22SAlex Crichton return _print_finished_future.valid() &&
127*33224b22SAlex Crichton _print_finished_future.wait_for(std::chrono::seconds(0)) !=
128*33224b22SAlex Crichton std::future_status::ready;
129*33224b22SAlex Crichton }
wait_for_print_result() const130*33224b22SAlex Crichton void wait_for_print_result() const { _print_finished_future.wait(); }
get_print_result()131*33224b22SAlex Crichton void get_print_result() { _print_finished_future.get(); }
set_print_success()132*33224b22SAlex Crichton void set_print_success() { _print_finished.set_value(); }
133*33224b22SAlex Crichton
134*33224b22SAlex Crichton private:
135*33224b22SAlex Crichton std::promise<int32_t> _value_to_print;
136*33224b22SAlex Crichton std::promise<void> _print_finished;
137*33224b22SAlex Crichton std::future<void> _print_finished_future;
138*33224b22SAlex Crichton };
139*33224b22SAlex Crichton
140*33224b22SAlex Crichton printer_thread_state printer_state;
141*33224b22SAlex Crichton
142*33224b22SAlex Crichton struct async_call_env {
143*33224b22SAlex Crichton wasm_trap_t **trap_ret;
144*33224b22SAlex Crichton };
145*33224b22SAlex Crichton
poll_print_finished_state(void * env)146*33224b22SAlex Crichton bool poll_print_finished_state(void *env) {
147*33224b22SAlex Crichton std::cout << "polling async host function result" << std::endl;
148*33224b22SAlex Crichton auto *async_env = static_cast<async_call_env *>(env);
149*33224b22SAlex Crichton // Don't block, just poll the future state.
150*33224b22SAlex Crichton if (printer_state.print_is_pending()) {
151*33224b22SAlex Crichton return false;
152*33224b22SAlex Crichton }
153*33224b22SAlex Crichton try {
154*33224b22SAlex Crichton printer_state.get_print_result();
155*33224b22SAlex Crichton } catch (const std::exception &ex) {
156*33224b22SAlex Crichton std::string msg = ex.what();
157*33224b22SAlex Crichton *async_env->trap_ret = wasmtime_trap_new(msg.data(), msg.size());
158*33224b22SAlex Crichton }
159*33224b22SAlex Crichton return true;
160*33224b22SAlex Crichton }
161*33224b22SAlex Crichton } // namespace
162*33224b22SAlex Crichton
main()163*33224b22SAlex Crichton int main() {
164*33224b22SAlex Crichton // A thread that will async perform host function calls.
165*33224b22SAlex Crichton std::thread printer_thread([]() {
166*33224b22SAlex Crichton int32_t value_to_print = printer_state.get_value_to_print();
167*33224b22SAlex Crichton std::cout << "received value to print!" << std::endl;
168*33224b22SAlex Crichton std::this_thread::sleep_for(std::chrono::milliseconds(500));
169*33224b22SAlex Crichton std::cout << "printing: " << value_to_print << std::endl;
170*33224b22SAlex Crichton std::this_thread::sleep_for(std::chrono::milliseconds(500));
171*33224b22SAlex Crichton std::cout << "signaling that value is printed" << std::endl;
172*33224b22SAlex Crichton printer_state.set_print_success();
173*33224b22SAlex Crichton });
174*33224b22SAlex Crichton
175*33224b22SAlex Crichton handle<wasmtime_error_t, wasmtime_error_delete> error;
176*33224b22SAlex Crichton
177*33224b22SAlex Crichton auto engine = create_engine();
178*33224b22SAlex Crichton auto store = create_store(engine.get());
179*33224b22SAlex Crichton // This pointer is unowned.
180*33224b22SAlex Crichton auto *context = wasmtime_store_context(store.get());
181*33224b22SAlex Crichton // Configure the store to periodically yield control
182*33224b22SAlex Crichton wasmtime_context_set_fuel(context, 100000);
183*33224b22SAlex Crichton wasmtime_context_fuel_async_yield_interval(context, /*interval=*/10000);
184*33224b22SAlex Crichton
185*33224b22SAlex Crichton auto compiled_module =
186*33224b22SAlex Crichton compile_wat_module_from_file(engine.get(), "examples/async.wat");
187*33224b22SAlex Crichton
188*33224b22SAlex Crichton auto linker = create_linker(engine.get());
189*33224b22SAlex Crichton static std::string host_module_name = "host";
190*33224b22SAlex Crichton static std::string host_func_name = "print";
191*33224b22SAlex Crichton
192*33224b22SAlex Crichton // Declare our async host function's signature and definition.
193*33224b22SAlex Crichton wasm_valtype_vec_t arg_types;
194*33224b22SAlex Crichton wasm_valtype_vec_t result_types;
195*33224b22SAlex Crichton wasm_valtype_vec_new_uninitialized(&arg_types, 1);
196*33224b22SAlex Crichton arg_types.data[0] = wasm_valtype_new_i32();
197*33224b22SAlex Crichton wasm_valtype_vec_new_empty(&result_types);
198*33224b22SAlex Crichton handle<wasm_functype_t, wasm_functype_delete> functype{
199*33224b22SAlex Crichton wasm_functype_new(&arg_types, &result_types)};
200*33224b22SAlex Crichton
201*33224b22SAlex Crichton error.reset(wasmtime_linker_define_async_func(
202*33224b22SAlex Crichton linker.get(), host_module_name.data(), host_module_name.size(),
203*33224b22SAlex Crichton host_func_name.data(), host_func_name.size(), functype.get(),
204*33224b22SAlex Crichton [](void *, wasmtime_caller_t *, const wasmtime_val_t *args, size_t,
205*33224b22SAlex Crichton wasmtime_val_t *, size_t, wasm_trap_t **trap_ret,
206*33224b22SAlex Crichton wasmtime_async_continuation_t *continuation_ret) {
207*33224b22SAlex Crichton std::cout << "invoking async host function" << std::endl;
208*33224b22SAlex Crichton printer_state.set_value_to_print(args[0].of.i32);
209*33224b22SAlex Crichton
210*33224b22SAlex Crichton continuation_ret->callback = &poll_print_finished_state;
211*33224b22SAlex Crichton continuation_ret->env = new async_call_env{trap_ret};
212*33224b22SAlex Crichton continuation_ret->finalizer = [](void *env) {
213*33224b22SAlex Crichton std::cout << "deleting async_call_env" << std::endl;
214*33224b22SAlex Crichton delete static_cast<async_call_env *>(env);
215*33224b22SAlex Crichton };
216*33224b22SAlex Crichton },
217*33224b22SAlex Crichton /*env=*/nullptr, /*finalizer=*/nullptr));
218*33224b22SAlex Crichton if (error) {
219*33224b22SAlex Crichton exit_with_error("failed to define host function", error.get(), nullptr);
220*33224b22SAlex Crichton }
221*33224b22SAlex Crichton
222*33224b22SAlex Crichton // Now instantiate our module using the linker.
223*33224b22SAlex Crichton handle<wasmtime_call_future_t, wasmtime_call_future_delete> call_future;
224*33224b22SAlex Crichton wasm_trap_t *trap_ptr = nullptr;
225*33224b22SAlex Crichton wasmtime_error_t *error_ptr = nullptr;
226*33224b22SAlex Crichton wasmtime_instance_t instance;
227*33224b22SAlex Crichton call_future.reset(wasmtime_linker_instantiate_async(
228*33224b22SAlex Crichton linker.get(), context, compiled_module.get(), &instance, &trap_ptr,
229*33224b22SAlex Crichton &error_ptr));
230*33224b22SAlex Crichton while (!wasmtime_call_future_poll(call_future.get())) {
231*33224b22SAlex Crichton std::cout << "yielding instantiation!" << std::endl;
232*33224b22SAlex Crichton }
233*33224b22SAlex Crichton error.reset(error_ptr);
234*33224b22SAlex Crichton handle<wasm_trap_t, wasm_trap_delete> trap{trap_ptr};
235*33224b22SAlex Crichton if (error || trap) {
236*33224b22SAlex Crichton exit_with_error("failed to instantiate module", error.get(), trap.get());
237*33224b22SAlex Crichton }
238*33224b22SAlex Crichton // delete call future - it's no longer needed
239*33224b22SAlex Crichton call_future = nullptr;
240*33224b22SAlex Crichton // delete the linker now that we've created our instance
241*33224b22SAlex Crichton linker = nullptr;
242*33224b22SAlex Crichton
243*33224b22SAlex Crichton // Grab our exported function
244*33224b22SAlex Crichton static std::string guest_func_name = "print_fibonacci";
245*33224b22SAlex Crichton wasmtime_extern_t guest_func_extern;
246*33224b22SAlex Crichton bool found =
247*33224b22SAlex Crichton wasmtime_instance_export_get(context, &instance, guest_func_name.data(),
248*33224b22SAlex Crichton guest_func_name.size(), &guest_func_extern);
249*33224b22SAlex Crichton assert(found);
250*33224b22SAlex Crichton assert(guest_func_extern.kind == WASMTIME_EXTERN_FUNC);
251*33224b22SAlex Crichton
252*33224b22SAlex Crichton // Now call our print_fibonacci function with n=15
253*33224b22SAlex Crichton std::array<wasmtime_val_t, 1> args;
254*33224b22SAlex Crichton args[0].kind = WASMTIME_I32;
255*33224b22SAlex Crichton args[0].of.i32 = 15;
256*33224b22SAlex Crichton std::array<wasmtime_val_t, 0> results;
257*33224b22SAlex Crichton call_future.reset(wasmtime_func_call_async(
258*33224b22SAlex Crichton context, &guest_func_extern.of.func, args.data(), args.size(),
259*33224b22SAlex Crichton results.data(), results.size(), &trap_ptr, &error_ptr));
260*33224b22SAlex Crichton // Poll the execution of the call. This can yield control back if there is an
261*33224b22SAlex Crichton // async host call or if we ran out of fuel.
262*33224b22SAlex Crichton while (!wasmtime_call_future_poll(call_future.get())) {
263*33224b22SAlex Crichton // if we have an async host call pending then wait for that future to finish
264*33224b22SAlex Crichton // before continuing.
265*33224b22SAlex Crichton if (printer_state.print_is_pending()) {
266*33224b22SAlex Crichton std::cout << "waiting for async host function to complete" << std::endl;
267*33224b22SAlex Crichton printer_state.wait_for_print_result();
268*33224b22SAlex Crichton std::cout << "async host function completed" << std::endl;
269*33224b22SAlex Crichton continue;
270*33224b22SAlex Crichton }
271*33224b22SAlex Crichton // Otherwise we ran out of fuel and yielded.
272*33224b22SAlex Crichton std::cout << "yield!" << std::endl;
273*33224b22SAlex Crichton }
274*33224b22SAlex Crichton // Extract if there were failures or traps after poll returns that execution
275*33224b22SAlex Crichton // completed.
276*33224b22SAlex Crichton error.reset(error_ptr);
277*33224b22SAlex Crichton trap.reset(trap_ptr);
278*33224b22SAlex Crichton if (error || trap) {
279*33224b22SAlex Crichton exit_with_error("running guest function failed", error.get(), trap.get());
280*33224b22SAlex Crichton }
281*33224b22SAlex Crichton call_future = nullptr;
282*33224b22SAlex Crichton // At this point, if our host function returned results they would be
283*33224b22SAlex Crichton // available in the `results` array.
284*33224b22SAlex Crichton std::cout << "async function call complete!" << std::endl;
285*33224b22SAlex Crichton
286*33224b22SAlex Crichton // Join our thread and exit.
287*33224b22SAlex Crichton printer_thread.join();
288*33224b22SAlex Crichton return 0;
289*33224b22SAlex Crichton }
290