-
Notifications
You must be signed in to change notification settings - Fork 349
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
!Send Future violates stacked borrow rules #3796
Comments
Isn't this a duplicate of #3780? If not, we'll need a self-contained reproducing example, ideally as small as possible. |
For #3780, I tried the recommended fix in tokio: tokio-rs/tokio#6744 Does not fix the issue. |
To replicate the issue run the following: #[test]
fn test_local_future() {
let (tx1, mut rx1) = tokio::sync::mpsc::channel::<usize>(1);
let (tx2, mut rx2) = tokio::sync::mpsc::channel::<usize>(1);
let (runnable_tx, runnable_rx) = std::sync::mpsc::channel();
let (runnable, task) = async_task::spawn_local(
async move {
let (a, b) = tokio::join!(rx1.recv(), rx2.recv());
assert_eq!(a, Some(10));
assert_eq!(b, Some(20));
},
move |runnable| {
runnable_tx.send(runnable).unwrap();
},
);
runnable.schedule();
task.detach();
let tick_cb = || {
runnable_rx.try_iter().for_each(|runnable| {
runnable.run();
});
};
tx1.try_send(10).unwrap();
tick_cb();
tick_cb();
tx2.try_send(20).unwrap();
tick_cb();
tick_cb();
} Toml file ...
[dependencies]
async-task = "4.7"
tokio = { version = "1", features = ["full"] }
|
I am not sure when I will have time to dig into this and figure out what exactly is happening. Meanwhile, if someone wants to help it'd be great to have this example minimized further until it does not have any dependencies any more. |
An attempt to minimize the example: fn main() {
let (tx1, mut rx1) = tokio::sync::mpsc::channel::<usize>(1);
let (runnable_tx, runnable_rx) = std::sync::mpsc::channel();
let (runnable, _task) = async_task::spawn_local(
async move {
rx1.recv().await;
},
move |runnable| {
runnable_tx.send(runnable).unwrap();
},
);
runnable.schedule();
runnable_rx.iter().next().unwrap().run();
tx1.try_send(20).unwrap();
runnable_rx.iter().next().unwrap().run();
} Note: It'd be nice if we can get rid of let (runnable, _task) = async_task::spawn_local(
async move {
rx1.recv().await;
},
move |runnable| {
runnable_tx.send(runnable).unwrap();
},
); so we can remove the |
Tested Seems like if recv channel has a Alternative example (without channels, but struct Data {
data: usize,
}
impl Data {
pub async fn mut_fn(&mut self) {
self.data += 1;
tokio::task::yield_now().await;
self.data += 2; // This causes the violation
}
}
#[test]
fn test_local_future_min() {
let mut my_test = Data { data: 10 };
let (runnable_tx, runnable_rx) = std::sync::mpsc::channel();
let (runnable, task) = async_task::spawn_local(
async move {
my_test.mut_fn().await;
},
move |runnable| {
runnable_tx.send(runnable).unwrap();
},
);
runnable.schedule();
task.detach();
runnable_rx.iter().next().unwrap().run();
runnable_rx.iter().next().unwrap().run();
} |
@RalfJung @tiif This seems like a duplicate of #3780 afaik. |
That is my theory. It's hard to test... or is there some easy way to make |
@RalfJung: Are you asking for, e.g., a way for a coroutine to emit a totally flat layout, where every field is at a different offset for testing? That should be possible. |
I was originally thinking of the niches of the coroutine itself, so that e.g. But your question made me realize that coroutines have their own discriminant, and that one should also not be using a niche for this. Having fields overlap is fine otherwise. |
This error persists even with rust-lang/rust#129313. So, it's not the same problem as #3780. |
I think I found a minimized version of the same problem: use std::{
future::Future,
pin::Pin,
sync::Arc,
task::{Context, Poll, Wake},
};
struct ThingAdder<'a> {
thing: &'a mut String,
}
impl Future for ThingAdder<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe {
*self.get_unchecked_mut().thing += ", world";
}
Poll::Pending
}
}
fn force_ref(_x: &impl Future) {}
fn main() {
let mut thing = "hello".to_owned();
let fut = async move { ThingAdder { thing: &mut thing }.await };
let mut fut = fut;
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
let waker = Arc::new(DummyWaker).into();
let mut ctx = Context::from_waker(&waker);
assert_eq!(fut.as_mut().poll(&mut ctx), Poll::Pending);
force_ref(&*fut); // Create a shared ref to the future itself, causing an aliasing model "fake" read.
assert_eq!(fut.as_mut().poll(&mut ctx), Poll::Pending);
}
struct DummyWaker;
impl Wake for DummyWaker {
fn wake(self: Arc<Self>) {}
} At least, this also errors with very similar symptoms (on terms of which kinds of references get created and invalidated in which order by which other accesses). My version also errors in Tree Borrows, unlike the original, but I think that is because the original happens to not write through a mutable reference again when it could. What's happening is the following:
In the example above, the problematic I think my conclusion is that such self-referenced fields must be treated like an |
So, I will close this as yet another duplicate of rust-lang/rust#63818 that will be resolved if/when someone implements |
FWIW, there is a possible work-around we could apply in Miri: to treat all But TBH I feel like piling more and more hacks on top of each other to avoid the consequences of rust-lang/rust#63818 is a waste of time. We know what the proper fix is, we just need to find someone willing and able to implement it. :) It is time to actually finish the so-far only half-finished integration of self-referential corotuines into the language. And in the future I hope we can avoid stabilizing features when they have such gaping holes... |
Tokio issue: tokio-rs/tokio#6750
Futures issue: rust-lang/futures-rs#2877
Explanation
The tests only fail when !Send future is used along with
tokio::join!
orfutures::join!
Miri message
The relevant debug logs are in the individual issues linked above
The text was updated successfully, but these errors were encountered: