Skip to content
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

wasm: scheduling of JavaScript callbacks is not fair #27894

Open
sherhut opened this issue Sep 27, 2018 · 11 comments
Open

wasm: scheduling of JavaScript callbacks is not fair #27894

sherhut opened this issue Sep 27, 2018 · 11 comments

Comments

@sherhut
Copy link

@sherhut sherhut commented Sep 27, 2018

What version of Go are you using (go version)?

go version go1.11 linux/amd64

Does this issue reproduce with the latest release?

yes

Description

I ran the webassembly benchmark in https://djhworld.github.io/post/2018/09/21/i-ported-my-gameboy-color-emulator-to-webassembly/ and as also noted on that page, the benchmark does not properly process keyboard input on chrome. See https://bugs.chromium.org/p/v8/issues/detail?id=8227 for the v8 tracking bug.

Due to a performance issue in v8, the compiled go code runs slower in v8 than firefox. As a consequence, there is more pressure on the go scheduler. In particular, there is nearly always a go-routine ready to be executed with very few idle pauses. Looking at the scheduler implementation at

if pauseSchedulerUntilCallback() {

this leads to starvation of the go-routines that are waiting for a callback from java-script. In case of the gameboy emulator, this means that no more I/O events are being processed.

@myitcv
Copy link
Member

@myitcv myitcv commented Sep 27, 2018

Is this in some way related to #26045?

@agnivade
Copy link
Contributor

@agnivade agnivade commented Sep 27, 2018

Possibly. But I am not very clear on what exactly this issue tries to resolve. Does pauseSchedulerUntilCallback perform a STW-like action where unless the callback hasn't happened, no other goroutine can execute ?

Otherwise, I don't see a reason other goroutines will still wait for a callback from js.

@sherhut
Copy link
Author

@sherhut sherhut commented Sep 27, 2018

The issue I am seeing here is not that go-routines cannot run while we wait for a callback, but that callbacks do not happen until no other go-routine runs. I just briefly looked at the runtime and might have missed something. But this is how I understand it:

Go (running on top of WebAssembly) can only receive callbacks from JavaScript if the WebAssembly runtime returns to the event loop. This is facilitated by pauseSchedulerUntilCallback, which in the go-runtime for JavaScript calls pause if any callbacks from JavaScript are potentially pending, that is a callback handler has been registered before. I looked at

// pauseSchedulerUntilCallback gets called from the scheduler and pauses the execution

The call to pause will fully unwind the stack and return from WebAssembly back to JavaScript land.

TEXT runtime·pause(SB), NOSPLIT, $0

Is there another path to fully unwind and return to JavaScript that I have missed?

@katiehockman katiehockman changed the title webassembly scheduling of JavaScript callbacks is not fair wasm: scheduling of JavaScript callbacks is not fair Sep 27, 2018
@katiehockman
Copy link
Member

@katiehockman katiehockman commented Sep 27, 2018

/cc @neelance

@neelance
Copy link
Member

@neelance neelance commented Oct 21, 2018

Yes, giving back execution from Go (WebAssembly) to JavaScript is a "stop the world" action for Go. No goroutine will run until a callback invokes Go code (user action, timer, etc.). Since we want all Go code to run which is not waiting for such callbacks, we give back execution to JS only if ALL goroutines are asleep. If your Go code always has something to do, execution will never be passed back to JS, so user events will not be processed.

This is the current situation. Maybe we could extend the scheduler so it periodically gives back execution to JS, even if there are still runnable goroutines.

This somewhat depends on #26045.

@agnivade
Copy link
Contributor

@agnivade agnivade commented Jun 2, 2019

@neelance - Just wanted to check in on this. Now that #26045 is resolved, has this situation improved, or it still needs further work ?

@neelance
Copy link
Member

@neelance neelance commented Jun 3, 2019

I think this needs further work. Most likely it will get resolved when we add WebAssembly threads.

@semaj
Copy link

@semaj semaj commented Oct 24, 2019

@neelance I think I'm running into this same issue, but I'm a bit confused by your explanation. How are goroutines scheduled in the browser environment? If there are 3 pending goroutines, does this equate to 3 pending events in the event queue? If it does, shouldn't an I/O event simply take its place in the event queue after the pending goroutines?

@neelance
Copy link
Member

@neelance neelance commented Oct 29, 2019

No, goroutines are not mapped to the event queue. If there are still goroutines awake, then the event queue will not get processed.

@semaj
Copy link

@semaj semaj commented Oct 29, 2019

Thanks for responding!

What constitutes "awake"? If one goroutine is blocked on a channel read and another is sleeping via time.Sleep, will Go immediately relinquish control to JavaScript to process pending events?

@neelance
Copy link
Member

@neelance neelance commented Oct 29, 2019

What constitutes "awake"? If one goroutine is blocked on a channel read and another is sleeping via time.Sleep, will Go immediately relinquish control to JavaScript to process pending events?

Yes, if there is no other goroutine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants
You can’t perform that action at this time.