-
Notifications
You must be signed in to change notification settings - Fork 158
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
[POC] Prototype async query functions #164
Conversation
I've been wondering for some time whether salsa should be based on async fn. I decided to start with just threads but I guess as async fn approaches stable this starts to be more plausible. |
Using plain sync functions is likely enough for most uses but I would really like to use async throughout the compiler to allow procedural macros to be async. Arguably async queries does make cancellation cleaner since it could be done as just not continuing to execute the future (for a query to check if its result is still needed it only needs to yield control). This experiment makes me quite confident both can supported with only a little overhead for the sync case however. |
Thanks for doing this @Marwes -- I'm wondering how much nicer it would be once rust-lang/rust#61775 lands (which should remove the need for lifetime parameters). |
Since it implements trait methods it needs to return a |
@Marwes ah, ok, perhaps not. |
c3766f7
to
d1b6867
Compare
Rebased this again. If async queries is something that is wanted I will try and get something decent done in time to the async/await release.
|
So there is an issue the If I remember correctly, this fn assert_send<T: Send>(t: T) -> T {
t
}
async fn function(_: &AsyncDatabase) {}
#[test]
fn test_send() {
assert_send(function(&AsyncDatabase::default()));
}
|
I also was thinking a little bit about async queries - and I think it could be nice to wait for structured concurrency. See example: tokio-rs/tokio#1879 |
About mutability - I think maybe we could be split methods into two traits
|
That might be nice since it could make it possible to ensure that sub-queries are done before the caller returns! Currently that requires an
Yep, I am leaning towards that as well. It is unfortunate that it infects the current, sync queries as well but I suspect that they will need that anway if they are to support running sub-queries in parallel. (https://salsa.zulipchat.com/#narrow/stream/145099-general/topic/Sync.20database) |
Wanted to check how much code would need to be added/duplicated to support async queries. This isn't a a full implementation (I haven't even tested to call async functions) but the normal sync implementation still work with this with what shouldn't be too much overhead. Not really interested in this until async/await gets to stable but figured a PR with this could still be useful.
DB: Database, | ||
{ | ||
fn drop(&mut self) { | ||
if !std::thread::panicking() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there is no unsafe invariant to uphold forgetting the Forker
just means that the query may end up in a bad state but there is no memory unsafety that is upheld by the drop.
So the PR as is basically works but it is in a rough shape. Highlights/issues atm.
I could use some feedback at this stage if/when there is some time. The fork + cycle check changes could plausibly be extracted and worked out separately and could solve #80 . |
Heh, @Marwes, I am always so negligent in providing feedback to your PRs. Thanks for the helpful summary. I'll try to give this a more detailed work. Some of the notes sounded mildly worrisome to me, but I have to look more deeply at the code to form a real opinion I guess. |
The cycle detection has been fixed (updated the notes) at least. |
I haven't had time for a detailed look, but thinking more about it I am quite nervous about using I guess one alternative is to use a proper lock? Or perhaps a async-aware lock? |
For async it is not enough with a lock, quoting myself.
For synchronous code, a But, if |
Another, alternate API may be to make |
OK, I see, maybe I hadn't deeply through the implications. You're saying that if you do let x = db.some_query(); you don't necessarily await the query right then -- hence something like This is a problem specifically for our internal stack tracking, I suppose, which is quite stateful... It's not (at least not obviously...) a problem from a more "theoretical" point of view, is it? I guess we have to be able to deal with the future being dropped, but that seems no different than generally being panic safe. |
Precisely.
Nope. Only the |
@Marwes ok. I feel like I've had branches where I started rewriting the tracking in that way...but I never landed them, obviously. I doubt it would be much overhead. |
I believe that the |
Wanted to check how much code would need to be added/duplicated to
support async queries. This isn't a a full implementation (I haven't
even tested to call async functions) but the normal sync implementation
still work with this with what shouldn't be too much overhead.
Not really interested in this until async/await gets to stable but
figured a PR with this could still be useful.