WebAssembly / wasm-c-api

Wasm C API prototype
Apache License 2.0
534 stars 77 forks source link

Support import globals callback #181

Closed ashtonmeuser closed 1 year ago

ashtonmeuser commented 1 year ago

Make it possible to fire a callback when getting or setting a variable import global via the Wasm C API.

Many Wasm runtimes expose this Wasm C API e.g. Wasmtime and Wasmer. These runtimes are often embedded in environments that require some glue between C/C++ and a higher-level language. With the current C API, there's no way to respond to a Wasm module altering a variable import global. Currently, global imports are just provided a wasm_val_t pointer to which they write a value.

Expected Behaviour

A way to provide optional getter/setter for an import global.

WASM_API_EXTERN own wasm_global_t* wasm_global_new(
  wasm_store_t*,
  const wasm_globaltype_t*,
  const wasm_val_t*,
  void (*setter)(uint32_t),
  uint32_t (*getter)()
);

I suppose getter/setter callbacks could alternatively be done at the wasm_val_t level.

rossberg commented 1 year ago

The semantics of globals are determined by the Wasm semantics, where they represent a flat memory cell. There is no notion of getters/setters on Wasm globals. Consequently, I don't see how the API you propose would be implementable without changing Wasm itself. Engines would have to know statically that a global has getters/setters to generate appropriate code AOT.

ashtonmeuser commented 1 year ago

@rossberg Thanks for clarifying. I suppose this is a fundamental misunderstanding of globals on my part. My reasoning was that imports live in the host while exports live in the guest Wasm module. For example, the source of truth for exported globals and functions lies in the guest. Conversely, imported functions exist in the host and the guest module must cross some FFI boundary to interact with them. Global imports seem to not abide by this mental model.

In that case, what is the practical difference between a mutable global import and a mutable global export? Without getter and setter callbacks, the host must implement these i.e. always query and set the value within the guest exactly as done with a global export. When multiple modules share the same imported global, the host must implement some consensus logic.

rossberg commented 1 year ago

Imports are not necessarily host-defined, they generally are exports from other Wasm modules. The host just has a way of "emulating" Wasm definitions.

In general, the "owner" of a global is the party who allocates it, i.e., a Wasm module or some host-side code. The others are essentially just given an address to the allocated memory space.

ashtonmeuser commented 1 year ago

I see. So if Module A "owns" a global, its value exists in Module A's memory. If that global is imported by Module B, the value is still only stored in A. Setting the value from B writes to A's memory. A host-defined import global must emulate memory which is then written to and read from by guest modules.

Is this correct?

rossberg commented 1 year ago

Yes, or at least that's a suitable intuition. How an engine actually implements this concretely is up to its implementation. But the idea is that global reads and writes compile to memory reads and writes.

ashtonmeuser commented 1 year ago

Thanks for clearing that up! Apologies for turning this into a Q&A. Closing.