Skip to content

NO_EXIT_RUNTIME by default #5878

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

Merged
merged 48 commits into from
Dec 16, 2017
Merged

NO_EXIT_RUNTIME by default #5878

merged 48 commits into from
Dec 16, 2017

Conversation

kripken
Copy link
Member

@kripken kripken commented Nov 30, 2017

This makes us not exit the runtime by default. That means we don't emit code for atexits and other things that happen when the runtime shuts down, like flushing the stdio streams. This is beneficial for 2 reasons:

  • For Better JS size for small programs #5794, this helps remove code. It avoids all the support for shutting down the runtime, emitting atexits, etc. It also enables more optimizations (the ctor evaller in wasm can do better without calls to atexit). This removes 3% of hello world's wasm size and 0.5% of its JS.
  • A saner default for the web. A program on the web that does anything asynchronous will not want the runtime to exit when main() exits, so we set this flag to 1 for many tests, which this PR lets us remove.

However, this is a breaking change. As already mentioned, the possible breakages are

  • printf("hello") will not console.log since there is no newline. Only when the streams are flushed would that be printed out. So this change would make us not emit that.
  • atexits do not run.

Both of those risks are mitigated in this PR:

  • In ASSERTIONS mode, check if there is unflushed stream output, and explain what to do if so.
  • Also in ASSERTIONS mode, warn if atexit is called.

This PR has a lot of test changes, some that simplify web tests - because the new default is better for the web - but others that add a param to a shell test - because the new default is less optimal in a shell environment. I think the risk here is lower than those shell tests indicate: we do test quite a lot of things in the shell, but just because it's convenient, not because that's what most users care about.

This PR uncovered 2 existing minor bugs:

  • An existing bug with our pthreads support: pthreads should always call the shutdown code, even with NO_EXIT_RUNTIME. Otherwise the main thread hangs waiting for them to complete. I think we never noticed this before since we never tested them with NO_EXIT_RUNTIME.
  • FORCE_FILESYSTEM didn't actually do what the name suggests. Again, I think we just never tested it properly with NO_EXIT_RUNTIME. Fixed in this PR.

… to get the exit code from the running program, as if it were a shell command, not a browser app
src/library.js Outdated
@@ -596,6 +596,11 @@ LibraryManager.library = {
atexit__proxy: 'sync',
atexit__sig: 'ii',
atexit: function(func, arg) {
#if ASSERTIONS
#if NO_EXIT_RUNTIME == 1
Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called. set NO_EXIT_RUNTIME to 0');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably use some more detail or a link to a doc. Also in C++, if the user has any static objects with destructors, then those destructors get registered with atexit under the hood. so the doc or message should probably mention that case explicitly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, added a FAQ entry about that, and mentioned the FAQ in those messages.

#endif
if (flush) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand why we are doing this dance with module.print. If it's ok to just call the users's fflush, why can't we just call it and let it flush for real?

Copy link
Member Author

@kripken kripken Dec 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't normally even include fflush anymore, we only include it in ASSERTIONS builds. So we don't want to show the proper output here, we want to see if there would be a problem, had we not been in an ASSERTIONS build. So we just check if flushing would have emitting something (and we do so very carefully, as shutting down the FS may throw an error if it hit an error previously or something like that).

I can add more of an explanation to the code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading that again it does seem less than obvious, good point. Added a comment.

…as we leave main(), then do not call exit, we are not exiting yet - code is yet to run later
@kripken
Copy link
Member Author

kripken commented Dec 1, 2017

This PR uncovered another existing bug, we used to call exit() when unwinding the stack in emterpreter-async mode. We shouldn't do that as more code will run, main() is not really finished. Fixed in that last commit.

src/library.js Outdated
@@ -596,6 +596,11 @@ LibraryManager.library = {
atexit__proxy: 'sync',
atexit__sig: 'ii',
atexit: function(func, arg) {
#if ASSERTIONS
#if NO_EXIT_RUNTIME == 1
Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME, so atexits() will not be called. set NO_EXIT_RUNTIME to 0 (see the FAQ)');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably say 'but NO_EXIT_RUNTIME is 1' (or 'is set') instead of just 'but NO_EXIT_RUNTIME'

src/postamble.js Outdated
#if NO_EXIT_RUNTIME
Module.printErr('exit(' + status + ') called, but compiled with NO_EXIT_RUNTIME, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)');
#else
Module.printErr('exit(' + status + ') called, but noExitRuntime, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done.

@dschuff
Copy link
Member

dschuff commented Dec 5, 2017

So, I am mostly convinced this is a good idea. I agree that most users would want this behavior and benefit from the size reduction. We currently (with or without this PR) have the situation that if you have NO_EXIT_RUNTIME there's no way to manually cause your destructors to run, right? (i.e. you want to shut down your module and flush, etc)? Has anyone complained about that?

@kripken
Copy link
Member Author

kripken commented Dec 5, 2017

We currently (with or without this PR) have the situation that if you have NO_EXIT_RUNTIME there's no way to manually cause your destructors to run, right?

Yes, the mode does not support global destructor calling at all.

Has anyone complained about that?

I can't remember any complaints. But to avoid confusion, we should warn about this in emscripten_force_exit, which advertises itself as being able to do that. I'll fix it.

@kripken
Copy link
Member Author

kripken commented Dec 5, 2017

Added that.

@kripken
Copy link
Member Author

kripken commented Dec 15, 2017

All feedback above should be addressed, and I have an offline lgtm from @juj, so I'll merge this as soon as tests pass. The last patch updates the metadce test, as after this change is is more effective, and the test expectations need to be updated.

@kripken
Copy link
Member Author

kripken commented Dec 16, 2017

Oddly the pthreads part of browser.test_emscripten_main_loop started to fail. @juj, this is the test we discussed, that we weren't sure why it was passing - now it makes sense and it fails ;)

I made it exit the runtime there, and now it passes. Is that ok?

I also added a commit with a faq entry on this stuff, and a better warning message (that also mentions that emitting a newline is enough to flush std streams).

@kripken
Copy link
Member Author

kripken commented Dec 16, 2017

Got on offline ok from @juj, and tests look good.

@kripken kripken merged commit f585dcb into incoming Dec 16, 2017
@kripken kripken deleted the no-exit-runtime branch December 16, 2017 19:06
@kripken kripken mentioned this pull request Jul 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants