Skip to content

TinyGo v0.35.0 breaks convention on Command modules for exported functions #4726

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
inliquid opened this issue Feb 9, 2025 · 3 comments
Closed
Labels
wasm WebAssembly

Comments

@inliquid
Copy link

inliquid commented Feb 9, 2025

Until very recently TinyGo had no support for compiling Reactor modules which have _initialize function as opposed to _start in Command modules. This, as well as some other reasons lead to community convention which worked for some years. In short according to WASI, Commands should exit after _start finished and as a result module with its exported functions becomes invalid.

However, TinyGo as well as some other compilers did not exit, which made it possible to call exported functions even after _start exited.

For instance Wazero, the most popular Wasm runtime in Go world has special note in documentation on this:

Why do we only return a sys.ExitError on a non-zero exit code?

It is reasonable to think an exit error should be returned, even if the code is success (zero). Even on success, the module is no longer functional. For example, function exports would error later. However, wazero does not. The only time sys.ExitError is on error (non-zero).

This decision was to improve performance and ergonomics for guests that both use WASI (have a _start function), and also allow custom exports. Specifically, Rust, TinyGo and normal wasi-libc, don't exit the module during _start. If they did, it would invalidate their function exports. This means it is unlikely most compilers will change this behavior.

GOOS=waspi1 from Go 1.21 does exit during _start. However, it doesn't support other exports besides _start, and _start is not defined to be called multiple times anyway.

Since sys.ExitError is not always returned, we added Module.IsClosed for defensive checks. This helps integrators avoid calling functions which will always fail.

There are well-established and popular tools and libraries in the wild which are built on top of this assumption, for instance https://github.com/knqyf263/go-plugin which generates helper stubs based on *.proto definitions. Generated code is based heavily on existing tools and their proven behavior, such as Wazero and TinyGo (pre-v0.35.0).

And a lot of tooling and code (including my own and the company I work in) is already in production.

This is good post by Matt Johnson-Pint in Slack which summarizes what's happened:

Hi folks. I've been trying out the recently released TinyGo 0.35.0 with Wazero and ran into some gotchas. Thought I would share my findings:

  • There's apparently two types of WASI programs (in WASI p1) - "commands" and "reactors".
  • The _start function is intended for use with "commands". In theory, it runs, and then the program is supposed to terminate.
  • In TinyGo 0.34.0, it never terminated, so you could call exported functions with Wazero without issues.
  • In TinyGo 0.35.0, _start runs, and then the equivalent of os.Exit(0) runs. So any Wazero function calls fail.
  • The solution is to build the app as a "reactor" by passing -buildmode=c-shared when compiling. That has the side effect of switching the startup function to _initialize - which must be called, but it doesn't exit.
  • In Wazero, you have to instantiate the module with configuration of .WithStartFunctions("_initialize") because the default is _start
    • Optionally you can pass .WithStartFunctions("_initialize", "_start") because Wazero will ignore any of those functions that don't exist. Then you can support both modes.

QQ: perhaps .WithStartFunctions("_initialize", "_start") should be the new default?

I understand that TinyGo is still in it's v0.*.* stage and can break some things, but during last releases it happens almost all the time, in almost every release.

This change it too much destructive. It's not that easy to re-implement all modules as the Reactors with changes all over community code. For example see this comment in go-plugin repo. I think as a consequence a lot of applications could stick to older TinyGo (pre-v0.35.0) forever.

I propose to revert this change, or at least give some time for community to adapt, providing compiler option to make TinyGo-compiled Wasm modules backwards-compatible with existing code.

@deadprogram deadprogram added the wasm WebAssembly label Feb 10, 2025
deadprogram added a commit that referenced this issue Feb 12, 2025
…xpected the

older behavior for wasi modules to not return an exit code as if they were reactors.

See #4726 for some details on what this is intended to address.

Signed-off-by: deadprogram <[email protected]>
@deadprogram
Copy link
Member

Please see #4734 for an attempt to address this.

deadprogram added a commit that referenced this issue Feb 18, 2025
…who expected the

older behavior for wasi modules to not return an exit code as if they were reactors.

See #4726 for some details on what this is intended to address.

Signed-off-by: deadprogram <[email protected]>
@inliquid
Copy link
Author

Thank you very much @deadprogram! Looking forward for PR being merged.

deadprogram added a commit that referenced this issue Feb 18, 2025
…who expected the

older behavior for wasi modules to not return an exit code as if they were reactors.

See #4726 for some details on what this is intended to address.

Signed-off-by: deadprogram <[email protected]>
deadprogram added a commit that referenced this issue Feb 21, 2025
…who expected the

older behavior for wasi modules to not return an exit code as if they were reactors.

See #4726 for some details on what this is intended to address.

Signed-off-by: deadprogram <[email protected]>
@deadprogram deadprogram added the next-release Will be part of next release label Feb 21, 2025
@deadprogram deadprogram removed the next-release Will be part of next release label Mar 4, 2025
@deadprogram
Copy link
Member

Closed as part of v0.36.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wasm WebAssembly
Projects
None yet
Development

No branches or pull requests

2 participants