WebAssembly / component-model

Repository for design and specification of the Component Model
Other
946 stars 79 forks source link

Add Canonical ABI option for opposite of `post-return` #289

Open esoterra opened 9 months ago

esoterra commented 9 months ago

The post-return canonopt provides a way for components to clean up allocated result values after the caller has read them.

It would also be valuable to provide components a way to set things up before the arguments are allocated and copied in. This could be achieved by adding another canonopt (named e.g. pre-param, pre-call, pre-alloc) which allows Components to specify a core function that is called at the beginning of a Component function call before parameters are allocated.

As a specific use case, toolchains may want to use a bump allocator whose lifecycle is tied to a single Component function call for function parameters, internal data, and return values. Using this new canonopt, it would be trivial to set it up before params are allocated and tear it down using post-return after return values have been consumed.

You can implement this currently by creating a bump allocator at startup time and then resetting it at post-return but I believe that when concurrency using async (and potentially threads/shared?) comes in and multiple calls (and as a result multiple bump allocators) are alive at the same time this simple pattern will no longer work.

lukewagner commented 9 months ago

Thanks for filing this idea and definitely the use case (call-scoped bump-allocation) makes sense.

In the Preview 2 timeframe, indeed you can approximate this feature by just using the realloc hook to lazily (on first call) mark the bump allocator and the post-return hook to reset to that mark (at the modest cost of one branch per realloc call), taking advantage of the component invariants which disallow reentrancy.

With native async in a Preview 3 timeframe where you can have multiple concurrent export calls in flight, indeed that does rules out this simple strategy, but it seems like a pre-call hook isn't quite enough -- you also need some sort of per-export-invocation "task-id" that is explicitly threaded through all the realloc and final post-return calls. For this and other reasons, I've been assuming that we'll need an ambiently-available/-supplied "task-index". Once we have that, then the same "lazy mark" strategy that works in Preview 2 could be applied in Preview 3 (making the lazy marking per-task instead of globally). But separately, I've realized that an asynchronously-callable export needs to be able to express backpressure before the call starts (b/c even just starting the call can consume non-trivial memory (e.g., for lists)), which suggests some async pre-call-like hook (for expressing backpressure), so perhaps we end up getting effectively what you're asking for as a matter of course in Preview 3.

esoterra commented 9 months ago

I think this has value in itself by simplifying the lifecycle management of allocators and other per-call resources for compiler codegen, but I agree that it really becomes important in Preview 3 as part of a fully-conceived task-id-based concurrent call lifecycle tracking enhancement to the canonical ABI.