bytecodealliance / wasmtime-go

Go WebAssembly runtime powered by Wasmtime
https://pkg.go.dev/github.com/bytecodealliance/wasmtime-go
Apache License 2.0
777 stars 75 forks source link

Epochs don't interrupt binaries with long time.Sleep durations #233

Open cedric-cordenier opened 1 day ago

cedric-cordenier commented 1 day ago

Hi!

I'm trying to execute the following go snippet compiled to WASM:

package main

import time

func main() {
    time.Sleep(10 * time.Second)
}

and am using an epoch deadline (set to "3"), which is being incremented every 100 milliseconds. In other words, the deadline for this is 300 milliseconds.

However, this isn't behaving as I expect using wasmtime-go: with the wasm trap only firing after the time.Sleep call has completed.

Is this expected behaviour?

Thanks!

alexcrichton commented 1 day ago

This is sort of expected behavior, sort of not. It's expected in that, yes, with wasmtime-go that's expected. This is a caveat of epochs where epochs are used to cancel executing wasm code but don't assist in cancelling host code. For cancelling host code that's left up to the embedder. The primary way of doing that is to use the async support that Wasmtime has, for example https://github.com/bytecodealliance/wasmtime/pull/9184 and https://github.com/bytecodealliance/wasmtime/issues/9188 are similar issues to the one here but for the wasmtime CLI (which switched over to using async).

So the "not expected" part is that Wasmtime does natively have support to fix this, but that comes back to the "this is expected" part in that the async parts of Wasmtime aren't exposed in Go at this time. There's a C API but it's not bound in Go yet.

cedric-cordenier commented 1 day ago

Hey @alexcrichton, thanks! Do you see a workaround to this issue? For imports that I can control, I think the workaround is straight-forward, we just need to ensure that we are respecting some timeout. However, AFAIU time.Sleep reduces to a call to the wasi syscall poll_oneoff which is not something I have control of. I can stub it out, but that raises the question generally of how we enforce timeouts on the wasi imports defined by wasmtime-go.

And just for my understanding: are you anticipating that support for these async parts of wasmtime will be released soon for wasmtime-go?

BTW if it's helpful I'd be happy to take a look at binding the relevant C API to Go if you can point me in the right direction?

alexcrichton commented 1 day ago

Yeah unfortunately there's not a great solution at this time because as you're seeing the imports in question are the ones provided by Wasmtime. You could theoretically define your own implementation of WASI and skip adding Wasmtime's own implementation, but even then you'd still have to orchestrate timeouts and such yourself (e.g. wake up the host function that's blocked on sleeping once the timeout elapses).

Currently there's no one working on adding support for the async APIs to Go, so that's an open project which if you'd like you can pick up. Exactly how it would integrate within Go I'm not sure myself, though, so it'll probably require a chunk of design work to figure out how to best fit within Go's idioms.