# Features

What is implemented, what is refused explicitly, and what is deferred. When a
feature is missing the runtime says so with an error; it does not approximate.

## Implemented

| Area | Status |
|---|---|
| Compile from binary or `.wat` text | yes, dirty CPU scheduler |
| WebAssembly proposals | Wasmtime 48 defaults, verified by compiling probe modules: bulk memory, reference types, multi-value, SIMD, relaxed SIMD, tail calls, extended const, multi-memory, memory64, exceptions, GC and typed function references, threads and shared memories |
| Instantiate with host functions | yes, function imports only |
| Call exports | yes, `i32`, `i64`, `f32`, `f64`, `v128` values |
| Traps | reported with `class => trap` and a `kind` per Wasmtime trap code |
| Interruption | `timeout` option and `interrupt/1`, epoch based, 10 ms granularity |
| Memory cap | `memory_limit`, default 256 MB, enforced by the store limiter |
| Table, element and instance caps | `max_tables`, `max_table_elements`, `max_instances` |
| Memory access from Erlang | `read_memory/3,4`, `write_memory/3,4`, `memory_size/1,2`: the export named `memory` (or the first exported memory) by default, any exported memory by name |
| Precompiled modules | `serialize/1` and `deserialize/1`, Wasmtime's `.cwasm` form; see [precompiled](precompiled.md) |
| Host functions in a dedicated process | `host => Pid` at instantiate, `handle_host_call/2` in that process |
| Non-blocking calls | `call_async/3` and `await/2,3` |
| Fuel metering | `compile(Bin, #{fuel => true})`, `call/4` with `fuel`, `fuel_remaining/1`; `kind => out_of_fuel` |
| Structured traps | `trace` on trap errors: `[#{func_index, func_offset, func_name, module_name}]`, innermost first |
| Validation without compiling | `validate/1`, `validate/2` with options |
| Compile options | `compile/2`: `opt_level` (`none`, `speed`, `speed_and_size`), `proposals` on or off, `fuel`; `module_options/1`; `deserialize/2` to pick the engine |
| Globals and tables from Erlang | `global_get/2`, `global_set/3`, `table_size/2`, `table_get/3`, `table_set/4`, `table_grow/3,4` |
| References across the boundary | `funcref`, `externref` and GC values as `ref()` terms, `null`, `{i31, N}`: in calls, host signatures, globals and tables; `externref/2` wraps a term; `call_ref/3,4`; `struct_get/set`, `array_len/get/set`; `gc/1`; see [references](references.md) |
| WASI stdio in memory | `stdin => {binary, _}`, `stdout`/`stderr => capture` with `read_output/1` and `output_limit`; `args`/`env => inherit` |
| Streams | `send/2`, `close/1`, `inbox_limit`; `stdin`/`stdout`/`stderr => stream` (a streamed stdout looks like a terminal to the guest, so lines leave as written); the `erlang.send` and `erlang.recv` imports; `{wasmtime_stream, Ref, Kind, Bytes}` to the `stream` process; see [streams](streams.md) |
| Runtime-only builds | `WASMTIME_RUNTIME_ONLY=1`: no compiler, 4 MB; `features/0` reports the linked library's capabilities; see [building](building.md) |
| Source build fallback | a platform without a prebuilt archive compiles the C API itself |
| WASI preview 1 | args, env, preopened dirs with read or write, stdio to file or inherited |
| Caller death | the abandoned call is interrupted, queued calls proceed |
| Host function timeout | `host_timeout`, default 30 s |

## Execution model

- One OS thread per instance owns the Wasmtime store. Calls are queued; one runs
  at a time.
- The calling process waits in `receive`; it is never blocked inside a NIF
  while the guest runs, so schedulers are not held.
- Host functions run in the calling process.
- One shared engine compiles every module; instances share nothing else.
- Erlang holds a handle; the instance itself is owned jointly by that handle
  and its thread. Dropping the handle tells the thread to stop and never
  blocks a scheduler. A caller that dies has its running call interrupted and
  its queued calls dropped.
- `timeout` cancels the request by id: its result is dropped in the NIF, so
  nothing lands in the mailbox afterwards. A result that arrived just as the
  timeout fired is returned as the answer.

## Refused explicitly

| Request | Error |
|---|---|
| Import the module does not provide | `class => link` |
| Non-function import from Erlang | `kind => unsupported_import` |
| `exnref` in a call or host signature, or `v128` and references in one signature | `kind => unsupported_type` |
| A reference used with another instance | `kind => wrong_instance` |
| `null` for a non-nullable type, a reference of the wrong family, an `{i31, N}` out of range | `kind => badarg` |
| Memory access while the guest runs | `kind => busy` |
| Memory access on an instance without memory | `kind => no_memory` |
| Out of range memory access | `kind => out_of_bounds` |
| Wrong argument count or type | `kind => badarity`, `kind => badarg` |
| A host function calling the instance it runs on | `kind => reentrant` |
| `compile/1`, `{wat, _}`, `serialize/1` or the `wasi` option on a build without them | `kind => unavailable` |
| `fuel` on a module compiled without metering | `kind => fuel_disabled` |
| A 33rd distinct compile option set | `kind => too_many_configurations` |
| `opt_level` other than `speed`, or a proposal the library lacks, on a build without it | `kind => unavailable` |
| Writing a constant global | `kind => immutable` |
| `send/2` past `inbox_limit`, or after `close/1` | `kind => inbox_full`, `kind => closed` |
| An `imports` entry for `erlang.send` or `erlang.recv` | `kind => reserved_import` |
| `erlang.send` or `erlang.recv` imported with another type | `kind => unsupported_type` |
| `stdin => stream` on a runtime-only build of a platform without a shim in `priv/shims` | `kind => unavailable` |

## Deferred

- **Creating GC structs and arrays from Erlang.** Values the guest creates
  can be read and written field by field; creating one needs a type handle
  the C API only gives for an existing value. Exception references
  (`exnref`) are not exposed.
- **Thread pool.** Measured on an M-series Mac: 13,900 instantiate, call and
  drop cycles per second from one process, 34,500 per second from eight, with
  one OS thread per instance. That covers "thousands of short-lived instances
  per second", so a pool is not planned unless a workload shows the thread
  cost first. Each thread reserves a 4 MB stack.
- **Fault isolation from Wasmtime itself.** A bug in Wasmtime would take the VM
  down, like any NIF. A `mode => port` running the instance in a separate OS
  process is the answer if that matters; same API, not built.
- **WASI preview 2 and components.** Not exposed.
- **Spawning guest threads.** The threads proposal validates and shared
  memories can be declared, but nothing lets a guest start a thread: there is
  no `wasi-threads` and no host function for it. A module is one thread.
