Skip to content

ASYNCIFY, EXIT_RUNTIME and std::process::exit() #21474

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
curiousdannii opened this issue Mar 5, 2024 · 7 comments · Fixed by #21554
Closed

ASYNCIFY, EXIT_RUNTIME and std::process::exit() #21474

curiousdannii opened this issue Mar 5, 2024 · 7 comments · Fixed by #21554

Comments

@curiousdannii
Copy link
Contributor

I've always compiled my project with both ASYNCIFY and EXIT_RUNTIME, but now that I'm mixing some Rust into it, it's not working as smoothly as before.

After my program has exited, by calling std::process::exit(0), it now raises these errors:

Aborted(Assertion failed: native function `stackRestore` called after runtime exit (use NO_EXIT_RUNTIME to keep it alive after main() exits))
Unhandled Rejection: RuntimeError: Aborted(Assertion failed: native function `stackRestore` called after runtime exit (use NO_EXIT_RUNTIME to keep it alive after main() exits))
    at abort (file:///home/dannii/emglken/build/glulxe.js:649:11)
    at assert (file:///home/dannii/emglken/build/glulxe.js:369:5)
    at file:///home/dannii/emglken/build/glulxe.js:698:5
    at invoke_v (file:///home/dannii/emglken/build/glulxe.js:2520:5)
    at imports.<computed> (file:///home/dannii/emglken/build/glulxe.js:1810:33)
    at glulxe.wasm.main (wasm://wasm/glulxe.wasm-05c7cc3a:wasm-function[6641]:0xb8080c)
    at ret.<computed> (file:///home/dannii/emglken/build/glulxe.js:1842:33)
    at Object.doRewind (file:///home/dannii/emglken/build/glulxe.js:1943:16)
    at file:///home/dannii/emglken/build/glulxe.js:1977:47
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

If I instead disable EXIT_RUNTIME, well now it seems to never actually resume after the first async import is called. Maybe that was always the case - does ASYNCIFY require EXIT_RUNTIME?

I'm not sure why it's trying to call doRewind after it's exited... Asyncify.state is 0.

Is Rust's std::process::exit() not really compatible with Emscripten, or ASYNCIFY? The equivalent code in my old C library was just exit(0).

@kripken
Copy link
Member

kripken commented Mar 14, 2024

Asyncify doesn't depend on EXIT_RUNTIME, AFAIK - most of our tests run without it actually.

If it's calling doRewind after an exit then perhaps you are pausing and then exiting during the pause, and then the resume happens automatically (after a timeout or such)?

@curiousdannii
Copy link
Contributor Author

curiousdannii commented Mar 17, 2024

All of my async library functions are completing.

I am manually calling callMain (with INVOKE_RUN=0) if that could make a difference.

@sbc100
Copy link
Collaborator

sbc100 commented Mar 18, 2024

The stackRestore is coming from an invoke_xxx function. In this case invoke_v. These functions are used for exception handling (and setjmp/longjjmp) so I suspend this issue could relate to exception handling.

Do you have some way to completely disable exceptions to verify this?

The invoke_v function is generated at build time here:

ret = '''\
function invoke_%s(%s) {
var sp = stackSave();
try {
%s
} catch(e) {
stackRestore(sp);
%s
_setThrew(1, 0);%s
}
}''' % (sig, ','.join(args), body, maybe_rethrow, exceptional_ret)

@sbc100
Copy link
Collaborator

sbc100 commented Mar 18, 2024

Yup, I can repro this locally by enabling exception catching can calling exit from within a try block:

$ cat test.cxx 
#include <stdio.h>
#include <stdlib.h>

void foo() {
  printf("foo\n");
  exit(1);
}

int main() {
  printf("main\n");
  try {
    foo();
  } catch(...) {
    printf("caught\n");
  }
}

sbc100 added a commit to sbc100/emscripten that referenced this issue Mar 18, 2024
The `stackRestore` function used by `invoke_xxx` wrappers and gets
called as the stack unwinds after a call to `exit`.

Alternatively we could add a check in each of the `invoke_xxx` wrappers
to avoid calling `stackRestore`, but that doesn't seem worth it.

Fixes: emscripten-core#21474
sbc100 added a commit to sbc100/emscripten that referenced this issue Mar 18, 2024
The `stackRestore` function used by `invoke_xxx` wrappers and gets
called as the stack unwinds after a call to `exit`.

Alternatively we could add a check in each of the `invoke_xxx` wrappers
to avoid calling `stackRestore`, but that doesn't seem worth it.

Fixes: emscripten-core#21474
@curiousdannii
Copy link
Contributor Author

I don't have exceptions enabled in my project (no -fexceptions.)

But we did have exceptions problems last time, which were seemingly solved by switching to C++ linking (even though it's Rust, not C++):
#21381

@sbc100
Copy link
Collaborator

sbc100 commented Mar 18, 2024

I don't have exceptions enabled in my project (no -fexceptions.)

But we did have exceptions problems last time, which were seemingly solved by switching to C++ linking (even though it's Rust, not C++): #21381

Yes, for whatever reason there is exception handling code being generated here. (either that or setjmp/longjmp).

If it is exception handling then you might want to investigate to see if you can remove it since it does some at a cost. Specifically it looks like the code you call is being compiled explicitly either with -fexceptions or with -sDISABLE_EXCEPTION_CATCHING=0.

sbc100 added a commit to sbc100/emscripten that referenced this issue Mar 18, 2024
The `stackRestore` function used by `invoke_xxx` wrappers and gets
called as the stack unwinds after a call to `exit`.

Alternatively we could add a check in each of the `invoke_xxx` wrappers
to avoid calling `stackRestore`, but that doesn't seem worth it.

Fixes: emscripten-core#21474
sbc100 added a commit that referenced this issue Mar 18, 2024
The `stackRestore` function used by `invoke_xxx` wrappers and gets
called as the stack unwinds after a call to `exit`.

Alternatively we could add a check in each of the `invoke_xxx` wrappers
to avoid calling `stackRestore`, but that doesn't seem worth it.

Fixes: #21474
@curiousdannii
Copy link
Contributor Author

curiousdannii commented Mar 18, 2024

Manually replacing the stackRestore line with this stops the errors, so I think your solution will work. Thank you!

var stackRestore = (a0) => (stackRestore = wasmExports['stackRestore'])(a0);

If it is exception handling then you might want to investigate to see if you can remove it since it does some at a cost. Specifically it looks like the code you call is being compiled explicitly either with -fexceptions or with -sDISABLE_EXCEPTION_CATCHING=0.

Neither of those are explicitly set. Perhaps set_target_properties(target PROPERTIES LINKER_LANGUAGE CXX) implicitly sets them? Or else it might be setjmp/longjmp.

Honestly, this is beyond me! Performance is already somewhat poor with ASYNCIFY. I'm just happy to have a working solution. 🙂

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 a pull request may close this issue.

3 participants