Make zero reads/writes' behavior right

This commit is contained in:
Ruihan Li
2025-06-15 19:24:53 +08:00
committed by Jianfeng Jiang
parent 837f908690
commit 4a9977d9a7
11 changed files with 226 additions and 18 deletions

View File

@ -60,12 +60,17 @@ impl Pollable for PipeReader {
impl FileLike for PipeReader {
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
let read_len = if self.status_flags().contains(StatusFlags::O_NONBLOCK) {
self.consumer.try_read(writer)?
if !writer.has_avail() {
// Even the peer endpoint (`PipeWriter`) has been closed, reading an empty buffer is
// still fine.
return Ok(0);
}
if self.status_flags().contains(StatusFlags::O_NONBLOCK) {
self.consumer.try_read(writer)
} else {
self.wait_events(IoEvents::IN, None, || self.consumer.try_read(writer))?
};
Ok(read_len)
self.wait_events(IoEvents::IN, None, || self.consumer.try_read(writer))
}
}
fn status_flags(&self) -> StatusFlags {
@ -130,6 +135,12 @@ impl Pollable for PipeWriter {
impl FileLike for PipeWriter {
fn write(&self, reader: &mut VmReader) -> Result<usize> {
if !reader.has_remain() {
// Even the peer endpoint (`PipeReader`) has been closed, writing an empty buffer is
// still fine.
return Ok(0);
}
if self.status_flags().contains(StatusFlags::O_NONBLOCK) {
self.producer.try_write(reader)
} else {

View File

@ -95,6 +95,14 @@ macro_rules! impl_common_methods_for_channel {
self.0.common.is_shutdown()
}
pub fn is_full(&self) -> bool {
self.this_end().rb().is_full()
}
pub fn is_empty(&self) -> bool {
self.this_end().rb().is_empty()
}
pub fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
self.this_end()
.pollee
@ -134,11 +142,10 @@ impl Producer<u8> {
/// - Returns `Ok(_)` with the number of bytes written if successful.
/// - Returns `Err(EPIPE)` if the channel is shut down.
/// - Returns `Err(EAGAIN)` if the channel is full.
///
/// The caller should not pass an empty `reader` to this method.
pub fn try_write(&self, reader: &mut dyn MultiRead) -> Result<usize> {
if reader.is_empty() {
// Even after shutdown, writing an empty buffer is still fine.
return Ok(0);
}
debug_assert!(!reader.is_empty());
if self.is_shutdown() {
return_errno_with_message!(Errno::EPIPE, "the channel is shut down");
@ -217,10 +224,10 @@ impl Consumer<u8> {
/// - Returns `Ok(_)` with the number of bytes read if successful.
/// - Returns `Ok(0)` if the channel is shut down and there is no data left.
/// - Returns `Err(EAGAIN)` if the channel is empty.
///
/// The caller should not pass an empty `writer` to this method.
pub fn try_read(&self, writer: &mut dyn MultiWrite) -> Result<usize> {
if writer.is_empty() {
return Ok(0);
}
debug_assert!(!writer.is_empty());
// This must be recorded before the actual operation to avoid race conditions.
let is_shutdown = self.is_shutdown();

View File

@ -125,6 +125,11 @@ pub trait Socket: private::SocketPrivate + Send + Sync {
impl<T: Socket + 'static> FileLike for T {
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
if !writer.has_avail() {
// Linux always returns `Ok(0)` in this case, so we follow it.
return Ok(0);
}
// TODO: Set correct flags
self.recvmsg(writer, SendRecvFlags::empty())
.map(|(len, _)| len)

View File

@ -142,6 +142,11 @@ where
warn!("sending control message is not supported");
}
if reader.is_empty() {
// Based on how Linux behaves, zero-sized messages are not allowed for netlink sockets.
return_errno_with_message!(Errno::ENODATA, "there are no data to send");
}
// TODO: Make sure our blocking behavior matches that of Linux
self.try_send(reader, remote.as_ref(), flags)
}

View File

@ -72,10 +72,24 @@ impl Connected {
}
pub(super) fn try_read(&self, writer: &mut dyn MultiWrite) -> Result<usize> {
if writer.is_empty() {
if self.reader.is_empty() {
return_errno_with_message!(Errno::EAGAIN, "the channel is empty");
}
return Ok(0);
}
self.reader.try_read(writer)
}
pub(super) fn try_write(&self, reader: &mut dyn MultiRead) -> Result<usize> {
if reader.is_empty() {
if self.writer.is_shutdown() {
return_errno_with_message!(Errno::EPIPE, "the channel is shut down");
}
return Ok(0);
}
self.writer.try_write(reader)
}