diff --git a/src/pty.rs b/src/pty.rs index 3142694..1997e33 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -283,7 +283,6 @@ fn poll_read( std::task::Poll::Ready(guard) => guard, std::task::Poll::Pending => return std::task::Poll::Pending, }?; - let prev_filled = buf.filled().len(); // SAFETY: we only pass b to read_buf, which never uninitializes any // part of the buffer it is given let b = unsafe { buf.unfilled_mut() }; @@ -298,7 +297,7 @@ fn poll_read( // initialized previously, or the call to read_buf did), and // assume_init will ignore any attempts to shrink the // initialized space, so this call is always safe. - unsafe { buf.assume_init(prev_filled + bytes) }; + unsafe { buf.assume_init(bytes) }; buf.advance(bytes); return std::task::Poll::Ready(Ok(())); } diff --git a/tests/basic.rs b/tests/basic.rs index 33f5d7a..637b471 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -68,3 +68,56 @@ async fn test_yes_async() { child.kill().await.unwrap() } + +#[cfg(feature = "async")] +#[tokio::test] +async fn test_buffer_initialization() { + use std::pin::Pin; + use std::task::{Context, Poll}; + use tokio::io::AsyncRead; + + let (mut pty, pts) = pty_process::open().unwrap(); + pty.resize(pty_process::Size::new(24, 80)).unwrap(); + let msg = "hello world"; + let mut child = pty_process::Command::new("echo") + .arg(msg) + .spawn(pts) + .unwrap(); + + let mut buf = [std::mem::MaybeUninit::uninit(); 64]; + + let mut read_buf = tokio::io::ReadBuf::uninit(&mut buf); + + let waker = futures::task::noop_waker(); + let mut cx = Context::from_waker(&waker); + + // first read: the whole output will fit into the buffer + loop { + match Pin::new(&mut pty).poll_read(&mut cx, &mut read_buf) { + Poll::Ready(Ok(())) => break, + Poll::Pending => { + tokio::time::sleep(std::time::Duration::from_millis(10)) + .await; + } + Poll::Ready(Err(e)) => panic!("Read failed: {}", e), + } + } + assert_eq!(read_buf.filled().len(), msg.len() + 2); // msg + "\r\n" + assert_eq!(read_buf.initialized().len(), read_buf.filled().len()); + + // second read: there will be no new data read + loop { + match Pin::new(&mut pty).poll_read(&mut cx, &mut read_buf) { + Poll::Ready(Ok(())) => break, + Poll::Pending => { + tokio::time::sleep(std::time::Duration::from_millis(10)) + .await; + } + Poll::Ready(Err(e)) => panic!("Read failed: {}", e), + } + } + assert_eq!(read_buf.filled().len(), msg.len() + 2); + assert_eq!(read_buf.initialized().len(), read_buf.filled().len()); + + child.wait().await.unwrap(); +}