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

file.cpp calls exit instead of throwing exception #4938

Closed
brson opened this issue Aug 18, 2022 · 7 comments · Fixed by #5087
Closed

file.cpp calls exit instead of throwing exception #4938

brson opened this issue Aug 18, 2022 · 7 comments · Fixed by #5087

Comments

@brson
Copy link
Contributor

brson commented Aug 18, 2022

I am calling binaryen's ModuleReader and ModuleWriter APIs from Rust and discovered that there are three cases in file.cpp where it calls exit. This isn't suitable for embedding in other applications. Can it be changed to throw an exception instead? Callers already need to deal with ParseException and MapParseException so this doesn't seem too burdensome a change.

If it's ok to change this to throw exceptions, would following the same pattern as MapException be acceptable? I am willing to make this change myself.

@kripken
Copy link
Member

kripken commented Aug 18, 2022

I think this makes sense. My one concern is that in general we are trying to move to have less exception-handling code in Binaryen, in particular so that the wasm port can be built with -fno-exceptions. But I think this change would not impact that, as in -fno-exceptions builds the exceptions would just exit or abort I guess. And there should be no other downside to using exceptions for ModuleReader/Writer errors AFAICT. But perhaps people that know more about C++ than me can confirm if there's really no downside though.

@brson
Copy link
Contributor Author

brson commented Aug 18, 2022

Not using exceptions is generally better for rust too, so if there's a different error handling pattern you'd like I'm happy to do that instead.

@kripken
Copy link
Member

kripken commented Aug 22, 2022

It doesn't seem like anyone else has strong opinions here, so I think using C++ exceptions for file.cpp is fine.

@tlively
Copy link
Member

tlively commented Aug 22, 2022

We have all kinds of error handling patterns in Binaryen. Another to consider would be something like BuildResult from wasm-type.h. https://github.com/WebAssembly/binaryen/blob/main/src/wasm-type.h#L610-L642. Alternatively, we could move Result and BuildResult (https://github.com/WebAssembly/binaryen/blob/main/src/wat-parser.h#L26-L67) to a more general header and use those, or we could pull in a std::expected implementation from C++23 (https://en.cppreference.com/w/cpp/header/expected).

I would mildly prefer any of those options to using exceptions, but also using exceptions here is certainly the simplest short-term fix. It would be nice to go through and standardize our error handling as a code health project at some point.

@brson
Copy link
Contributor Author

brson commented Aug 30, 2022

Pulling the Result and MaybeResult types out of wat-parser.h and reusing them looks like something I can handle. I don't quite understand how to use BuildResult on first read though.

@brson
Copy link
Contributor Author

brson commented Sep 18, 2022

After spending a day working on this I discovered the Fatal type, which is used in quite a few places, some of which also are possibly places it might be desirable for the process to not abort in an embedded context.

For my purposes, as an alternative to refactoring binaryen to propagate more non-exception errors, which will make the code increasingly more verbose, I could also convert the errors in file-io.cpp to use Fatal, and just apply a simple patch to convert Fatal to throw an exception that I can catch.

I know from the comments in Fatal that the function is expected to break deadlocks in some cases, which would not happen if converted to a throw, so there are some tradeoffs.

@tlively
Copy link
Member

tlively commented Sep 19, 2022

Updating those exits to use Fatal instead sounds good to me.

kripken pushed a commit that referenced this issue Oct 1, 2022
…#5087)

This patch makes binaryen easier to call from other applications by making more errors recoverable instead of early-exiting.

The main thing it does is change three calls to exit on I/O errors into calls to Fatal(), which is an existing custom abstraction for handling unrecoverable errors. Currently Fatal's destructor calls _Exit(1).

My intent is to make it possible for Fatal to not exit, but to throw, allowing an embedding application to catch the exception.

Because the previous early exits were exiting with error code EXIT_FAILURE, I also changed Fatal to exit with EXIT_FAILURE. The test suite continues to pass so I assume this is ok.

Next I changed Fatal to buffer its error message until the destructor instead of immediately printing it to stderr. This is for ease of patching Fatal to throw instead.

Finally, I also included the patch I need to make Fatal throw when THROW_ON_FATAL is defined at compile time. I can carry this patch out of tree, but it is a small patch, so perhaps you will be willing to take it. I am happy to remove it.

Fixes #4938
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