Skip to content
EN FR

ABI ownership and release

Documentation status: reference — see Maturity and evidence.

Ownership is data

Every ABI value descriptor carries ownership semantics. The current model can represent:

  • value;
  • caller-managed;
  • owned by caller;
  • owned by module;
  • borrowed;
  • retained.

It separately records how a releasable value must be returned:

  • no release;
  • module release;
  • module free;
  • a dedicated release method.

These fields are essential to language bindings because native pointer shape alone cannot tell the wrapper what to do.

Tested owned-handle path

The current executor tests certify this lifecycle:

constructor invocation
 -> handle result
 -> ownership = owned-by-caller
 -> release kind = module release
 -> use handle for instance calls
 -> explicit release
 -> wrapper no longer owns a native pointer

A generated .NET owning wrapper should map this to deterministic disposal. It should not call a general host allocator and should not depend on finalization as the normal release path.

Borrowed versus owned

A borrowed handle must not be released merely because the managed wrapper becomes unreachable. Conversely, an owned handle must not silently leak because the API forgot to preserve the descriptor ownership.

The safe binding rule is therefore:

descriptor ownership
 -> generated wrapper policy
 -> deterministic release behavior

not:

pointer-looking value
 -> guess ownership

Module-allocated text and buffers

The native error path demonstrates allocator symmetry. The module returns a UTF-8 error pointer; the host copies the text and returns that buffer through the same module's free function.

The same principle should govern future public strings, byte buffers, arrays, and records whenever memory crosses the module boundary.

The proposed first contracts for UTF-8 strings and byte buffers therefore use copy-then-module-free semantics for module-produced data.

Release safety

A release operation should be idempotent at the wrapper level even if the underlying ABI requires exactly one release call. After successful release, the wrapper should clear its native reference and reject further operations requiring a live object.

Runtime affinity

An opaque handle is meaningful only to the runtime/module that created it. Generated bindings should preserve this affinity and reject attempts to use a handle with a different client runtime or module context.