-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
I wonder if there is any atomicity violation in poll_ready #2429
Comments
Probably.... short answer is: don't do it 😄 There are bigger issues w/ concurrently calling I looked at the fns and was surprised there was no documentation calling this out. Would you mind submitting a PR adding some documentation stating that the caller must ensure that the functions are not being concurrently called? |
Note that for types such as tokio/tokio/src/net/tcp/stream.rs Lines 638 to 679 in a3aab86
|
The following safe code calls //main.rs
#![feature(async_closure)]
use tokio::io::PollEvented;
use futures::ready;
use mio::Ready;
use mio::net::{TcpStream, TcpListener};
use std::io;
use std::task::{Context, Poll};
use std::net::SocketAddr;
use futures::future::poll_fn;
use std::sync::Arc;
use std::thread;
struct MyListener {
poll_evented: PollEvented<TcpListener>,
}
impl MyListener {
pub fn poll_accept(&self, cx: &mut Context<'_>) -> Poll<Result<TcpStream, io::Error>> {
let ready = Ready::readable();
ready!(self.poll_evented.poll_read_ready(cx, ready))?;
// poll_read_ready() shall not be concurrently called
match self.poll_evented.get_ref().accept() {
Ok((socket, _)) => Poll::Ready(Ok(socket)),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.poll_evented.clear_read_ready(cx, ready)?;
Poll::Pending
}
Err(e) => Poll::Ready(Err(e)),
}
}
}
async fn foo() {
let addr = "127.0.0.1:8000".parse::<SocketAddr>().unwrap();
let listener1= Arc::new(MyListener {
poll_evented: PollEvented::new(TcpListener::bind(&addr).unwrap()).unwrap(),
});
let listener2 = Arc::clone(&listener1);
poll_fn(|cx| listener1.poll_accept(cx)).await.unwrap(); // poll_read_ready() called in thread 1.
let th = thread::spawn(async move ||
poll_fn(move |cx| listener2.poll_accept(cx)).await // poll_read_ready() called in thread 2.
);
th.join().unwrap().await.unwrap();
}
#[tokio::main]
async fn main() {
foo().await
}
|
Calling it concurrently will mess up mio's internal state, but it shouldn't cause UB. My previous comment on |
When the safety of a function depends on its caller, it does not seem that safe. |
Safety means something very specific in Rust. That said, I think the fact that the concern leaked out is not ideal. Fixing it requires a breaking change though, so until 0.3, docs is the best we can do. |
…okio-rs#2439) Co-authored-by: Alice Ryhl <alice@ryhl.io> Fixes: tokio-rs#2429
Version
0.2.18
Platform
64-bit Windows 10
Description
By reading the source code, I wonder if there is any atomicity violation in macro
poll_ready
in tokio/src/io/poll_evented.rstokio/tokio/src/io/poll_evented.rs
Lines 125 to 160 in 43bbbf6
The above logic can be simplified to
If two threads T1&T2 are executing simultaneously:
T1: 1, 2, 3 (if ret.is_empty() is false)
T2: 1, 2, 3, 4 (update self.cache, maybe now ret.is_empty() is true)
T1: 4 (Still consider ret.is_empty() is false and update self.cache accordingly)
The problem is load() and store() are seperated and
the execution of store() is dependent on load().
Therefore, if load() and store() are interleaved by another store(), a possible atomicity violation happens.
I wonder if this is an issue. If not, how does the code logic avoid it? Thank you.
The text was updated successfully, but these errors were encountered: