Skip to content

Allow goroutines from exported wasm functions #3095

Open

Description

@codefromthecrypt

Currently, the following will work as expected:

package main

import "fmt"

func main() {
	msg := make(chan int)
	finished := make(chan int)
	go func() {
		<-msg
		fmt.Println("consumer")
		finished <- 1
	}()
	go func() {
		fmt.Println("producer")
		msg <- 1
	}()
	<-finished
}

However, doing the same from an exported function besides main panics at the line the goroutine is created.

package main

import "fmt"

func main() {}

//export notMain
func notMain() {
	msg := make(chan int)
	finished := make(chan int)
	go func() { // <- panic
		<-msg
		fmt.Println("consumer")
		finished <- 1
	}()
	go func() {
		fmt.Println("producer")
		msg <- 1
	}()
	<-finished
}

Since Wasm isn't parallel-safe, we rely on keeping main alive and calling the exported function at the same time.
It would seem helpful to have a compiler flag or an annotation like //enable-scheduler

Note: jimmysl lee noticed that if you manipulate the %.wasm and insert a call opcode to call the _start function prior to actually invoking the desired export, it doesn't crash. This hint might help narrow down a path to a solution.

Activity

  1. aykevl commented on Aug 25, 2022

    @aykevl
    Member

    If you want to call an exported function without calling _start first: that is not possible. _start must be called first to initialize various things (the runtime, global variables, etc).

    Or do you mean something else?

    It might be possible to allow calling exported functions after main has returned, if that is what you want.

  2. codefromthecrypt commented on Aug 25, 2022

    @codefromthecrypt
    ContributorAuthor

    _start is already called first (at least in the test I was doing as wazero calls it by default, so before you can call any exports). Perhaps it is due to _start completing?

  3. aykevl commented on Sep 8, 2022

    @aykevl
    Member

    I don't think this can work, at least not in a JS environment. The issue is that JavaScript can't block, and you are doing a blocking operation with <-finished.

    Apart from that, running goroutines after main has returned isn't specified in the Go specification:

    Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

    In other words, goroutines that were running when main exits will be terminated. That doesn't necessarily imply that you can't start new goroutines, but it would be odd if that were possible.

  4. codefromthecrypt commented on Sep 9, 2022

    @codefromthecrypt
    ContributorAuthor

    ok I should rephrase this as non-js. ex wasi and freestanding cc @hunjixin

    The nature of entry points in wasm are different than go, and the impedance mismatch is a reality. what we do about it however is our choice. IOTW saying something about how main works in normal go, is historical context, but in the context of wasm (non js.. remember wasm core spec has no JS in it), it isn't necessarily painting a clear decision of what to do.

  5. dgryski commented on Feb 2, 2023

    @dgryski
    Member

    Does this boil down to #2735 ?

  6. codefromthecrypt commented on Feb 2, 2023

    @codefromthecrypt
    ContributorAuthor

    I'm not really sure the future of the reactor thing in WASI, even normal command is being completely redone. It feels uncomfortable to move it to blocked on something we don't implement, and might be dead by the time we do..

  7. twharmon commented on Jan 22, 2024

    @twharmon
    Contributor

    I encountered a very similar problem with wasm ran in the browser. Rather than panic, the goroutine would just not execute. I was able to "fix" it by wrapping it in a js func:

    var work = js.FuncOf(somethingThatUsesGoroutines)
    
    //export notMain
    func notMain() {
    	work.Invoke()
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestwasmWebAssembly

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions