Fix poll behavior

This commit is contained in:
Ruihan Li 2024-09-01 21:48:03 +08:00 committed by Tate, Hongliang Tian
parent 479d98c8b9
commit 6151d65cf5
2 changed files with 74 additions and 42 deletions

View File

@ -1,8 +1,5 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
#![allow(dead_code)]
#![allow(unused_variables)]
use core::{ use core::{
borrow::Borrow, borrow::Borrow,
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
@ -149,7 +146,7 @@ impl EpollFile {
.ok_or_else(|| { .ok_or_else(|| {
Error::with_message(Errno::ENOENT, "fd is not in the interest list") Error::with_message(Errno::ENOENT, "fd is not in the interest list")
})?; })?;
entry.update(new_ep_event, new_ep_flags); entry.update(new_ep_event, new_ep_flags)?;
entry.clone() entry.clone()
}; };
@ -232,20 +229,22 @@ impl EpollFile {
continue; continue;
}; };
let (ep_event, ep_flags) = entry.event_and_flags(); let (has_ready_events, ep_flags) = match entry.poll() {
// If this entry's file is ready, save it in the output array. // If this entry's file is ready, the events need to be saved in the output array.
// EPOLLHUP and EPOLLERR should always be reported. Some((ready_events, user_data, ep_flags)) if !ready_events.is_empty() => {
let ready_events = entry.poll() & (ep_event.events | IoEvents::HUP | IoEvents::ERR); ep_events.push(EpollEvent::new(ready_events, user_data));
count_events += 1;
// Records the events from the ready list (true, ep_flags)
if !ready_events.is_empty() { }
ep_events.push(EpollEvent::new(ready_events, ep_event.user_data)); // If the file is not ready, there is nothing to do.
count_events += 1; Some((_, _, ep_flags)) => (false, ep_flags),
} // If the file no longer exists, the entry should be removed.
None => (false, EpollFlags::ONE_SHOT),
};
// If there are events and the epoll entry is neither edge-triggered // If there are events and the epoll entry is neither edge-triggered
// nor one-shot, then we should keep the entry in the ready list. // nor one-shot, then we should keep the entry in the ready list.
if !ready_events.is_empty() if has_ready_events
&& !ep_flags.intersects(EpollFlags::ONE_SHOT | EpollFlags::EDGE_TRIGGER) && !ep_flags.intersects(EpollFlags::ONE_SHOT | EpollFlags::EDGE_TRIGGER)
{ {
ready.push_back(weak_entry); ready.push_back(weak_entry);
@ -285,11 +284,11 @@ impl Pollable for EpollFile {
// Implement the common methods required by FileHandle // Implement the common methods required by FileHandle
impl FileLike for EpollFile { impl FileLike for EpollFile {
fn read(&self, writer: &mut VmWriter) -> Result<usize> { fn read(&self, _writer: &mut VmWriter) -> Result<usize> {
return_errno_with_message!(Errno::EINVAL, "epoll files do not support read"); return_errno_with_message!(Errno::EINVAL, "epoll files do not support read");
} }
fn write(&self, reader: &mut VmReader) -> Result<usize> { fn write(&self, _reader: &mut VmReader) -> Result<usize> {
return_errno_with_message!(Errno::EINVAL, "epoll files do not support write"); return_errno_with_message!(Errno::EINVAL, "epoll files do not support write");
} }
@ -407,38 +406,30 @@ impl EpollEntry {
self.key.file.upgrade().map(KeyableArc::into) self.key.file.upgrade().map(KeyableArc::into)
} }
/// Get the epoll event associated with the epoll entry.
pub fn event(&self) -> EpollEvent {
let inner = self.inner.lock();
inner.event
}
/// Get the epoll flags associated with the epoll entry.
pub fn flags(&self) -> EpollFlags {
let inner = self.inner.lock();
inner.flags
}
/// Get the epoll event and flags that are associated with this epoll entry.
pub fn event_and_flags(&self) -> (EpollEvent, EpollFlags) {
let inner = self.inner.lock();
(inner.event, inner.flags)
}
/// Poll the events of the file associated with this epoll entry. /// Poll the events of the file associated with this epoll entry.
/// ///
/// If the returned events is not empty, then the file is considered ready. /// If the returned events is not empty, then the file is considered ready.
pub fn poll(&self) -> IoEvents { pub fn poll(&self) -> Option<(IoEvents, u64, EpollFlags)> {
match self.file() { let file = self.file()?;
Some(file) => file.poll(IoEvents::all(), None),
None => IoEvents::empty(), let (event, flags) = {
} let inner = self.inner.lock();
(inner.event, inner.flags)
};
Some((file.poll(event.events, None), event.user_data, flags))
} }
/// Update the epoll entry, most likely to be triggered via `EpollCtl::Mod`. /// Update the epoll entry, most likely to be triggered via `EpollCtl::Mod`.
pub fn update(&self, event: EpollEvent, flags: EpollFlags) { pub fn update(&self, event: EpollEvent, flags: EpollFlags) -> Result<()> {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
*inner = EpollEntryInner { event, flags }
if let Some(file) = self.file() {
file.register_observer(self.self_weak(), event.events)?;
}
*inner = EpollEntryInner { event, flags };
Ok(())
} }
/// Returns whether the epoll entry is in the ready list. /// Returns whether the epoll entry is in the ready list.

View File

@ -50,3 +50,44 @@ FN_TEST(epoll_add_del)
TEST_SUCC(close(rfd2)); TEST_SUCC(close(rfd2));
} }
END_TEST() END_TEST()
FN_TEST(epoll_mod)
{
int fildes[2];
int epfd, rfd, wfd;
struct epoll_event ev;
char buf[1];
// Setup pipes
TEST_SUCC(pipe(fildes));
rfd = fildes[0];
wfd = fildes[1];
TEST_SUCC(write(wfd, "", 1));
// Setup epoll
epfd = TEST_SUCC(epoll_create1(0));
ev.events = EPOLLOUT;
ev.data.fd = rfd;
TEST_SUCC(epoll_ctl(epfd, EPOLL_CTL_ADD, rfd, &ev));
// Wait for EPOLLOUT
TEST_RES(epoll_wait(epfd, &ev, 1, 0), _ret == 0);
// Modify the events
ev.events = EPOLLIN;
ev.data.fd = rfd;
TEST_SUCC(epoll_ctl(epfd, EPOLL_CTL_MOD, rfd, &ev));
// Wait for EPOLLIN
TEST_RES(epoll_wait(epfd, &ev, 1, 0), _ret == 1);
TEST_SUCC(read(rfd, buf, 1));
TEST_RES(epoll_wait(epfd, &ev, 1, 0), _ret == 0);
TEST_SUCC(write(wfd, "", 1));
TEST_RES(epoll_wait(epfd, &ev, 1, 0), _ret == 1);
// Clean up
TEST_SUCC(close(epfd));
TEST_SUCC(close(rfd));
TEST_SUCC(close(wfd));
}
END_TEST()