Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions os/src/kernel/syscall/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ fn wait_for_would_block(file: Arc<dyn File>, task: crate::kernel::SharedTask) ->
Ok(())
}

fn file_read_ready(file: &Arc<dyn File>) -> bool {
if let Some(pipe_file) = file.as_any().downcast_ref::<crate::vfs::PipeFile>() {
return pipe_file.read_ready();
}
file.readable()
}

fn file_write_ready(file: &Arc<dyn File>) -> bool {
if let Some(pipe_file) = file.as_any().downcast_ref::<crate::vfs::PipeFile>() {
return pipe_file.write_ready();
}
file.writable()
}
Comment on lines +87 to +99

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The helper functions file_read_ready and file_write_ready currently take &Arc<dyn File> as their parameter. However, they only need to perform operations on the underlying dyn File trait object (via as_any() and readable() / writable()).

By changing the parameter type to &dyn File, we decouple these helper functions from the Arc smart pointer, making them more idiomatic, flexible, and reusable. Deref coercion will automatically handle passing &Arc<dyn File> at the call sites without requiring any modifications there.

Suggested change
fn file_read_ready(file: &Arc<dyn File>) -> bool {
if let Some(pipe_file) = file.as_any().downcast_ref::<crate::vfs::PipeFile>() {
return pipe_file.read_ready();
}
file.readable()
}
fn file_write_ready(file: &Arc<dyn File>) -> bool {
if let Some(pipe_file) = file.as_any().downcast_ref::<crate::vfs::PipeFile>() {
return pipe_file.write_ready();
}
file.writable()
}
fn file_read_ready(file: &dyn File) -> bool {
if let Some(pipe_file) = file.as_any().downcast_ref::<crate::vfs::PipeFile>() {
return pipe_file.read_ready();
}
file.readable()
}
fn file_write_ready(file: &dyn File) -> bool {
if let Some(pipe_file) = file.as_any().downcast_ref::<crate::vfs::PipeFile>() {
return pipe_file.write_ready();
}
file.writable()
}


/// 向文件描述符写入数据
pub fn write(fd: usize, buf: *const u8, count: usize) -> isize {
loop {
Expand Down Expand Up @@ -671,11 +685,11 @@ fn poll_with_timeout(
}
};

if (pollfd.events & POLLIN) != 0 && file.readable() {
if (pollfd.events & POLLIN) != 0 && file_read_ready(&file) {
pollfd.revents |= POLLIN;
}

if (pollfd.events & POLLOUT) != 0 && file.writable() {
if (pollfd.events & POLLOUT) != 0 && file_write_ready(&file) {
pollfd.revents |= POLLOUT;
}

Expand Down Expand Up @@ -889,14 +903,14 @@ fn select_common(

let mut fd_ready = false;
if check_read
&& file.readable()
&& file_read_ready(&file)
&& let Some(ref mut set) = read_set
{
set.set(fd);
fd_ready = true;
}
if check_write
&& file.writable()
&& file_write_ready(&file)
&& let Some(ref mut set) = write_set
{
set.set(fd);
Expand Down
21 changes: 18 additions & 3 deletions os/src/kernel/syscall/network/connection_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,12 @@ pub fn send(sockfd: i32, buf: *const u8, len: usize, _flags: i32) -> isize {
return n as isize;
}
Err(e) => {
pr_debug!("send: sockfd={}, len={} -> error={:?}", sockfd, chunk_len, e);
pr_debug!(
"send: sockfd={}, len={} -> error={:?}",
sockfd,
chunk_len,
e
);
if e == crate::vfs::FsError::WouldBlock {
if let Some(socket_file) = file.as_any().downcast_ref::<SocketFile>()
&& !socket_file.flags().contains(OpenFlags::O_NONBLOCK)
Expand Down Expand Up @@ -325,11 +330,21 @@ pub fn recv(sockfd: i32, buf: *mut u8, len: usize, _flags: i32) -> isize {

match result {
Ok(n) => {
pr_debug!("recv: sockfd={}, len={} -> received={}", sockfd, chunk_len, n);
pr_debug!(
"recv: sockfd={}, len={} -> received={}",
sockfd,
chunk_len,
n
);
return n as isize;
}
Err(e) => {
pr_debug!("recv: sockfd={}, len={} -> error={:?}", sockfd, chunk_len, e);
pr_debug!(
"recv: sockfd={}, len={} -> error={:?}",
sockfd,
chunk_len,
e
);
if e == crate::vfs::FsError::WouldBlock {
if let Some(socket_file) = file.as_any().downcast_ref::<SocketFile>()
&& !socket_file.flags().contains(OpenFlags::O_NONBLOCK)
Expand Down
10 changes: 9 additions & 1 deletion os/src/kernel/syscall/network/socket_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,12 +499,20 @@ fn replenish_tcp_listeners(
while network_stack().tcp_spare_listener_count(socket_file, listen_endpoint) < target {
let new_listen_handle = match create_tcp_socket() {
Ok(SocketHandle::Tcp(h)) => h,
Err(e) => return Err(e.to_errno()),
Err(e) => {
if network_stack().tcp_spare_listener_count(socket_file, listen_endpoint) > 0 {
break;
}
return Err(e.to_errno());
}
Ok(SocketHandle::Udp(_)) => return Err(-(crate::uapi::errno::EINVAL as isize)),
};

if let Err(e) = network_stack().tcp_listen(new_listen_handle, listen_endpoint) {
network_stack().remove_tcp_socket(new_listen_handle);
if network_stack().tcp_spare_listener_count(socket_file, listen_endpoint) > 0 {
break;
}
return Err(e.to_errno());
}

Expand Down
4 changes: 3 additions & 1 deletion os/src/net/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ impl SocketFile {

pub(crate) fn udp_push(&self, d: UdpDatagram) -> bool {
let mut q = self.udp_rx_queue.lock();
if q.len() == q.capacity() {
if q.len() >= UDP_RXQ_MAX_CAP {
let _ = q.pop_front();
} else if q.len() == q.capacity() {
let old_capacity = q.capacity();
if old_capacity < UDP_RXQ_MAX_CAP {
let new_capacity = old_capacity
Expand Down
15 changes: 10 additions & 5 deletions os/src/vfs/impls/pipe_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,18 +276,23 @@ impl PipeFile {
buffer.set_capacity(new_size)?;
Ok(buffer.get_capacity())
}

pub fn read_ready(&self) -> bool {
self.end_type.readable() && self.buffer.lock().can_read_now()
}

pub fn write_ready(&self) -> bool {
self.end_type.writable() && self.buffer.lock().can_write_now()
}
Comment on lines +284 to +286

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When the read end of a pipe is closed, any subsequent write to the pipe will fail immediately with EPIPE / BrokenPipe rather than blocking. Therefore, the write end of the pipe should be considered write-ready (i.e., poll / select should return immediately with POLLOUT instead of blocking indefinitely).

Currently, write_ready only checks can_write_now(), which returns false if read_end_count == 0. This causes writer processes to hang indefinitely in poll / select once the reader exits.

We should update write_ready to also return true if the reader was previously present but has now closed its end (ever_had_reader && read_end_count == 0).

    pub fn write_ready(&self) -> bool {
        if !self.end_type.writable() {
            return false;
        }
        let buf = self.buffer.lock();
        buf.can_write_now() || (buf.ever_had_reader && buf.read_end_count == 0)
    }

}

impl File for PipeFile {
fn readable(&self) -> bool {
self.end_type.readable() && self.buffer.lock().can_read_now()
self.end_type.readable()
}

fn writable(&self) -> bool {
if !self.end_type.writable() {
return false;
}
self.buffer.lock().can_write_now()
self.end_type.writable()
}

fn read(&self, buf: &mut [u8]) -> Result<usize, FsError> {
Expand Down
Loading