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

Implement "opinionated cancellation" #265

Merged
merged 11 commits into from
May 27, 2021
Merged

Implement "opinionated cancellation" #265

merged 11 commits into from
May 27, 2021

Conversation

jonas-schievink
Copy link
Contributor

This implements the design described in RFC #262.

Currently, the in_par_get_set_cancellation test is failing. I'm not completely sure what it is trying to test (the comment on the test does not match its behavior), so I couldn't fix it.

@jonas-schievink
Copy link
Contributor Author

rust-analyzer companion PR: rust-lang/rust-analyzer#8866

src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated
}
}

impl std::error::Error for Canceled {}
Copy link
Member

Choose a reason for hiding this comment

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

I think we should have this, but this was not obvious to me, so let me write down justification.

I would expect Cancellation to not be treated as an error. Cancellation is a serendipitous success. It would be wrong to propagate Cancelled as Error. However, in salsa itself Cancelled is not used throughout the API, as we use unwinding, its used only at the boundady. For boundaries, I think treating it as an Error is probably fine

src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Show resolved Hide resolved
src/runtime.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
@matklad
Copy link
Member

matklad commented May 17, 2021 via email

@lnicola
Copy link
Contributor

lnicola commented May 17, 2021

Yeah, but there's also the more American "cancelation" which matches "canceled", while the code used "cancellation". TBH, the American spelling hurts my eyes 😅.

src/derived/slot.rs Outdated Show resolved Hide resolved
src/runtime.rs Outdated Show resolved Hide resolved
src/runtime.rs Outdated
@@ -38,6 +41,8 @@ pub struct Runtime {

/// Shared state that is accessible via all runtimes.
shared_state: Arc<SharedState>,

on_cancelation_check: Option<Box<dyn Fn() + RefUnwindSafe + Send>>,
Copy link
Member

Choose a reason for hiding this comment

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

Hm, why do we need this? I imagined this can work the same way as other events, by user overriding on_salsa_event or some such.

Copy link
Member

Choose a reason for hiding this comment

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

salsa/src/lib.rs

Lines 67 to 73 in b3cb3c4

/// This function is invoked at key points in the salsa
/// runtime. It permits the database to be customized and to
/// inject logging or other custom behavior.
fn salsa_event(&self, event_fn: Event) {
#![allow(unused_variables)]
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That needs a Database, which unwind_if_canceled doesn't have. Should we pass the db as an argument here?

Copy link
Member

Choose a reason for hiding this comment

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

Hm, perhaps we should make unwind_if_canceled a method of the Database then? That would actually give a nice way to opt-out of unwinding -- overrride it to be no-op. Not sure if I love or hate that flexibility though.

Copy link
Member

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

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

This looks basically good to me -- I wasn't clear @jonas-schievink on whether there are broken tests? I guess I can check CI :

src/derived/slot.rs Outdated Show resolved Hide resolved
src/runtime.rs Outdated
assert_eq!(pending_revision, current_revision);
self.report_anon_read(pending_revision);
false
self.unwind_canceled();
Copy link
Member

Choose a reason for hiding this comment

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

This simplification is quite nice.

Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit worried that there could be a subtle bug if we allowed users to override cancelation here.

src/runtime.rs Outdated

/// Registers a callback to be invoked every time [`Runtime::unwind_if_canceled`] is called
/// (either automatically by salsa, or manually by user code).
pub fn set_cancelation_check_callback<F>(&mut self, callback: F)
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 know why, but "cancellation" is far more common...

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @matklad that it would be nice to do this via salsa_event -- do we have a concrete use for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The primary use case is to assert that no more than N milliseconds pass between cancellation checks, which is helpful for debugging long-running queries that affect rust-analyzer responsiveness

@nikomatsakis
Copy link
Member

can you try running the tests with RUST_BACKTRACE=1? I'm confused where the error originates :(

@jonas-schievink
Copy link
Contributor Author

Test failure backtrace:

thread 'frozen::in_par_get_set_cancelation' panicked at 'called `Result::unwrap()` on an `Err` value: Any { .. }', tests/parallel/frozen.rs:64:33
stack backtrace:
   0: rust_begin_unwind
             at /rustc/881c1ac408d93bb7adaa3a51dabab9266e82eee8/library/std/src/panicking.rs:493:5
   1: core::panicking::panic_fmt
             at /rustc/881c1ac408d93bb7adaa3a51dabab9266e82eee8/library/core/src/panicking.rs:92:14
   2: core::result::unwrap_failed
             at /rustc/881c1ac408d93bb7adaa3a51dabab9266e82eee8/library/core/src/result.rs:1355:5
   3: core::result::Result<T,E>::unwrap
             at /home/jonas/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:1037:23
   4: parallel::frozen::in_par_get_set_cancelation
             at ./tests/parallel/frozen.rs:64:18
   5: parallel::frozen::in_par_get_set_cancelation::{{closure}}
             at ./tests/parallel/frozen.rs:13:1
   6: core::ops::function::FnOnce::call_once
             at /home/jonas/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
   7: core::ops::function::FnOnce::call_once
             at /rustc/881c1ac408d93bb7adaa3a51dabab9266e82eee8/library/core/src/ops/function.rs:227:5

@nikomatsakis
Copy link
Member

@jonas-schievink hmm that's not very useful. I was hoping for a backtrace more in the spawned threads. I'll try to run myself.

@nikomatsakis
Copy link
Member

OK, so the error seeems-- ah, I know what's going on. Maybe it was obvious to you, @jonas-schievink, but it wasn't to me. What is happening is that the call to db.input('a') in the first thread is now checking the cancellation flag and hence you get cancellation. Before we were intentionally swallowing it, which you can no longer do. I think this test can just be modified to remove the logic after the catch_unwind code entirely?

@jonas-schievink jonas-schievink marked this pull request as ready for review May 25, 2021 13:55
Copy link
Member

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

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

This looks great!

src/lib.rs Outdated
/// Cancellation will automatically be triggered by salsa on any query
/// invocation.
///
/// This method should not be overridden by `Database` implementors.
Copy link
Member

Choose a reason for hiding this comment

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

We should mention that it invokes salsa_event with WillCheckCancellation, so implementors can override that to intercept.

@nikomatsakis
Copy link
Member

Oh, I guess I should have marked that as "Request changes". Apart from the documentation nit, r=me

@matklad
Copy link
Member

matklad commented May 27, 2021

bors r=nikomatsakis

@bors
Copy link
Contributor

bors bot commented May 27, 2021

Build succeeded:

@bors bors bot merged commit e17e77c into salsa-rs:master May 27, 2021
@jonas-schievink jonas-schievink deleted the opinionated-cancellation branch May 27, 2021 12:33
matklad added a commit to matklad/salsa that referenced this pull request May 29, 2021
- new cancellation API salsa-rs#265
bors bot added a commit to rust-lang/rust-analyzer that referenced this pull request May 31, 2021
8866: Update salsa r=matklad a=jonas-schievink

This updates salsa to include salsa-rs/salsa#265, and removes all cancellation-related code from rust-analyzer

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
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.

None yet

5 participants