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

Use Result over catch_unwind for cancellation and cycle handling #572

Closed
wants to merge 10 commits into from

Conversation

MichaReiser
Copy link
Contributor

@MichaReiser MichaReiser commented Sep 1, 2024

Draft PR to switch Salsa from using catch_unwind to Result for "unwinding" the stack when a query is canceled or when handling cycles.

Relevant Zulip Discussion

The primary motivation for switching to Result is that WASM doesn't support catch_unwind. There are other upsides to using Result:

  • Applications can decide whether they want to use panic = abort to get better performance and smaller binaries
  • The API is more explicit about the fact that queries and most read operations may fail. I do find the Result helpful in that it makes it more obvious to users where a salsa operation can fail. For example, creating inputs or reading the fields of an interned struct never fail. Creating a tracked struct or accessing a tracked struct's fields can fail. Returning a Result also makes it more obvious that it may be necessary to handle the failure case to avoid logical errors (e.g. when creating multiple inputs and doing queries in-between)

The primary downsides of switching to Result are:

  • There's a 1-2% performance regression across all benchmarks (because of how Rust compiles try?)
  • Requires that all queries return salsa::Result<T> which leads to a less ergonomic API

TODO:

  • Add test that demonstrates that suppressing a cycle or cancelled error inside of a query panics

Should we use one or multiple result types?

The current implementation uses a single salsa::Result type with error variants for cycle-handling and cancellation. I generally prefer having a single error type because it relieves me of having to think about the right Result type for this specific method, nor do I have to change the Result type if I add one more accessor. However, the downside is that accessors on salsa structs have a too generic Result type.

Should we use async await instead?

@davidbarsky has floated this idea multiple times. I don't understand async Rust well enough to outline a design using async and await for cancellation (I suspect that a design supporting cancellation should also work for handling cycles). Buck2's dice framework uses async await and seems to support cancellation. So this might be very interesting to explore. Using async would be even more interesting if we could use the same infrastructure to support concurrency (by scheduling work as task).

The main downside of using async is that it's a very heavyweight dependency and probably also comes with a fair amount of overhead which might be especially problematic for fine-grained queries and getters on salsa structs.

Copy link

netlify bot commented Sep 1, 2024

Deploy Preview for salsa-rs canceled.

Name Link
🔨 Latest commit bb6bbdd
🔍 Latest deploy log https://app.netlify.com/sites/salsa-rs/deploys/66d6e5cbb806860008b59e81

Copy link

codspeed-hq bot commented Sep 1, 2024

CodSpeed Performance Report

Merging #572 will not alter performance

Comparing MichaReiser:result (bb6bbdd) with master (2af849b)

Summary

✅ 8 untouched benchmarks

@michalmuskala
Copy link

There's the additional loss of DevX for queries that today already return Option or Result since they won't be able ot use ? anymore - dealing with nested Result always ends up very verbose

@MichaReiser
Copy link
Contributor Author

There's the additional loss of DevX for queries that today already return Option or Result since they won't be able ot use ? anymore - dealing with nested Result always ends up very verbose

Definitely, and that's the biggest counterargument to using Result or was the primary motivation for using catch_unwind in the past. Unfortunately, catch_unwind isn't supported by wasm :(

@MichaReiser MichaReiser marked this pull request as ready for review September 2, 2024 16:36
@MichaReiser
Copy link
Contributor Author

Hmm, not sure what the miri errors are about.

Comment on lines +265 to +267
impl<T, E> HasOutput for std::result::Result<T, E> {
type Output = T;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is too bad that Specialization is a nightly-only feature. It would allow us to implement HasOutput for T and specialize it for Result. It would allow the macro to support both non-result and result return types when adding a new into_result(self) -> salsa::Result<Self::Output> method (which is a no-op for salsa::Result and returns Ok(self) for all other types.

@nikomatsakis
Copy link
Member

The primary motivation here is WASM support -- it's probably worth poking the WASM folks too to understand if there are other options.

@MichaReiser
Copy link
Contributor Author

MichaReiser commented Sep 6, 2024

@nikomatsakis do you know any WASM folks we could reach out to? If I remember correctly, didn't you @BurntSushi mention in one of our 1:1s that you know a WASM expert?

@nikomatsakis
Copy link
Member

Yeah. I can do that but I've also been thinking over what async would look like. It might work out ok, though it's not obvious to me yet how to manage cycle recovery without something like panic. It's actually an interesting gap in Rust's future trait I hadn't considered: we would need some way to "peel" a layer off the top. I guess the fact that we control all tracked functions might help.

Reviewing this PR definitely made me want to find alternatives for WASM support :)

@MichaReiser
Copy link
Contributor Author

Our overall sentiment is that we want to explore an async/await-based solution. We can come back to this if that exploration is unsuccessful.

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.

3 participants