Is there any documentation giving details about how the actual epoch injection takes place? IE at what stages and how?
Thanks!
This method is the only one that updates the epoch: https://docs.rs/wasmtime/latest/wasmtime/struct.Engine.html#method.increment_epoch
that links to the docs on the config option, which covers the whole flow of methods to call and what happens when
I meant how is the code instrumented. I'm under the impression that code to "check" the epoch is added? How does that process work? The API documentation is quite good I'm just trying to understand whats going on under the hood.
Yes, that's right, we insert checks -- they happen at loop backedges and at function entry
we don't document the "how" in the public docs because it's an internal implementation detail, but basically: we load the current epoch and the deadline from memory and compare, branching to a call back into the runtime (that subsequently traps or async-yields) if the deadline's exceeded
current epoch is an atomic u64
; and increment_epoch
is an atomic increment, nothing more, which is how we made it signal-safe and thread-safe
Okay thanks!
At what point are the checks injected? At compile time?
Yes; there would be no way to inject them later, as we don't do runtime code patching
@Chris Fallin do you think it makes sense to add this documentation to the increment_epoch()
or maybe Config::epoch_interruption
might be the better place since there's a decent chunk of documenation there.
If so, @Nicholas Renner would you mind adding the documentation you would have wanted to see to wasmtime
?
@Victor Adossi I don't think it really makes sense to put in the public API documentation -- the question was "how did we implement it" and we generally don't put implementation details in public docs (as it's irrelevant to the audience that only wants to use wasmtime). Perhaps in the architecture doc somewhere (docs/contributing-architecture.md
)?
I had some more specific questions, still trying to understand some of this:
@Nicholas Renner have you looked into the implementation? It might be more efficient to read the code if you have specific questions about how this works. Grep for epoch_interruption
and go from there; crates/cranelift/
contains the Wasm-to-CLIF translator. (It would also help us to know whether you have already read the code and are still unsure, or if these questions are your starting point, to calibrate level of detail.)
It would also help to know to what end these questions are directed -- are you trying to build a feature that integrates with this? Understand performance implications? Other?
That said: injection is not exactly either of your options -- we do not inject Wasm bytecode, and we do not edit the cwasm or operate after the compiler. Rather we inject compiler IR when we translate the Wasm to CLIF. Control is passed to the runtime via a Wasm-to-host call; yielding occurs via our async mechanism that does stack switching (see the "fiber" crate).
might be worth adding @Nicholas Renner that - while you cannot output the WASM with epoch instrumentation as WASM - you can output the clif IR https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.emit_clif in case you want to just look at it or study its impact on code behavior or something
Thanks all,
Yeah, I'm still coming in somewhat blind. I mostly wanted to be pointed to the sections in the implementation to start looking at so thanks for that. I'm building something that uses asyncify with wasmtime for our project's use, I know thats pretty out of scope for wasmtime in general, but was trying to find some information to see how epochs would interact with the asyncify mechanisms.
Ah! That's a much more specific and easily-answerable question: epoch interruption should not interfere with Wasm semantics at all, including Wasm semantics utilized by asyncify. It operates one level down, invisible to the Wasm execution: we basically split the execution into pieces each of which runs in one future-poll invocation in the Wasmtime host-side async interface.
Integration between what guest code calls "async" and what host code calls "async" is a much deeper question, and one that is unrelated to epoch interruption: that veers into WASI 0.3 and async work around component interfaces (talk to @Joel Dice about that one)
(said more briefly: if what you're looking for is to reflect async details in the guest into the host side, epoch interruption is not what you want -- it serves a different purpose)
Great, thank you very much for the responses. Going to take some time to look this over and may have some more questions, but this was super helpful!
Last updated: Dec 23 2024 at 12:05 UTC