mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-20 23:36:34 +00:00
Don't allocate when possible in do_poll
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
185b27b01c
commit
a6b3a65fe5
@ -1,6 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use alloc::borrow::Cow;
|
|
||||||
use core::{cell::Cell, time::Duration};
|
use core::{cell::Cell, time::Duration};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
@ -61,28 +60,17 @@ pub fn sys_poll(fds: Vaddr, nfds: u64, timeout: i32, ctx: &Context) -> Result<Sy
|
|||||||
|
|
||||||
pub fn do_poll(poll_fds: &[PollFd], timeout: Option<&Duration>, ctx: &Context) -> Result<usize> {
|
pub fn do_poll(poll_fds: &[PollFd], timeout: Option<&Duration>, ctx: &Context) -> Result<usize> {
|
||||||
let mut file_table = ctx.thread_local.file_table().borrow_mut();
|
let mut file_table = ctx.thread_local.file_table().borrow_mut();
|
||||||
let (result, files) = if let Some(file_table_inner) = file_table.get() {
|
|
||||||
hold_files(poll_fds, file_table_inner, Cow::Borrowed)
|
let poll_files = if let Some(file_table_inner) = file_table.get() {
|
||||||
|
PollFiles::new_borrowed(poll_fds, file_table_inner)
|
||||||
} else {
|
} else {
|
||||||
let file_table_locked = file_table.read();
|
let file_table_locked = file_table.read();
|
||||||
hold_files(poll_fds, &file_table_locked, |file| {
|
PollFiles::new_owned(poll_fds, &file_table_locked)
|
||||||
Cow::Owned(file.clone())
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
match result {
|
|
||||||
FileResult::AllValid => (),
|
|
||||||
FileResult::SomeInvalid => {
|
|
||||||
return Ok(count_all_events(poll_fds, &files));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let poller = match register_poller(poll_fds, files.as_ref()) {
|
let poller = match poll_files.register_poller() {
|
||||||
PollerResult::AllRegistered(poller) => poller,
|
PollerResult::Registered(poller) => poller,
|
||||||
PollerResult::EventFoundAt(index) => {
|
PollerResult::FoundEvents(num_events) => return Ok(num_events),
|
||||||
let next = index + 1;
|
|
||||||
let remaining_events = count_all_events(&poll_fds[next..], &files[next..]);
|
|
||||||
return Ok(1 + remaining_events);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -96,7 +84,7 @@ pub fn do_poll(poll_fds: &[PollFd], timeout: Option<&Duration>, ctx: &Context) -
|
|||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
let num_events = count_all_events(poll_fds, &files);
|
let num_events = poll_files.count_events();
|
||||||
if num_events > 0 {
|
if num_events > 0 {
|
||||||
return Ok(num_events);
|
return Ok(num_events);
|
||||||
}
|
}
|
||||||
@ -105,92 +93,108 @@ pub fn do_poll(poll_fds: &[PollFd], timeout: Option<&Duration>, ctx: &Context) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FileResult {
|
struct PollFiles<'a> {
|
||||||
AllValid,
|
poll_fds: &'a [PollFd],
|
||||||
SomeInvalid,
|
files: CowFiles<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds all the files we're going to poll.
|
enum CowFiles<'a> {
|
||||||
fn hold_files<'a, F, R>(
|
Borrowed(&'a FileTable),
|
||||||
poll_fds: &[PollFd],
|
Owned(Vec<Option<Arc<dyn FileLike>>>),
|
||||||
file_table: &'a FileTable,
|
}
|
||||||
f: F,
|
|
||||||
) -> (FileResult, Vec<Option<R>>)
|
|
||||||
where
|
|
||||||
F: Fn(&'a Arc<dyn FileLike>) -> R,
|
|
||||||
{
|
|
||||||
let mut files = Vec::with_capacity(poll_fds.len());
|
|
||||||
let mut result = FileResult::AllValid;
|
|
||||||
|
|
||||||
for poll_fd in poll_fds.iter() {
|
impl<'a> PollFiles<'a> {
|
||||||
let Some(fd) = poll_fd.fd() else {
|
/// Creates `PollFiles` by holding the file table reference.
|
||||||
files.push(None);
|
fn new_borrowed(poll_fds: &'a [PollFd], file_table: &'a FileTable) -> Self {
|
||||||
continue;
|
Self {
|
||||||
};
|
poll_fds,
|
||||||
|
files: CowFiles::Borrowed(file_table),
|
||||||
let Ok(file) = file_table.get_file(fd) else {
|
}
|
||||||
poll_fd.revents.set(IoEvents::NVAL);
|
|
||||||
result = FileResult::SomeInvalid;
|
|
||||||
|
|
||||||
files.push(None);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
files.push(Some(f(file)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(result, files)
|
/// Creates `PollFiles` by cloning all files that we're going to poll.
|
||||||
|
fn new_owned(poll_fds: &'a [PollFd], file_table: &FileTable) -> Self {
|
||||||
|
let files = poll_fds
|
||||||
|
.iter()
|
||||||
|
.map(|poll_fd| {
|
||||||
|
poll_fd
|
||||||
|
.fd()
|
||||||
|
.and_then(|fd| file_table.get_file(fd).ok().cloned())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
poll_fds,
|
||||||
|
files: CowFiles::Owned(files),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PollerResult {
|
enum PollerResult {
|
||||||
AllRegistered(Poller),
|
Registered(Poller),
|
||||||
EventFoundAt(usize),
|
FoundEvents(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers the files with a poller, or exits early if some events are detected.
|
impl PollFiles<'_> {
|
||||||
fn register_poller(poll_fds: &[PollFd], files: &[Option<Cow<Arc<dyn FileLike>>>]) -> PollerResult {
|
/// Registers the files with a poller, or exits early if some events are detected.
|
||||||
let mut poller = Poller::new();
|
fn register_poller(&self) -> PollerResult {
|
||||||
|
let mut poller = Poller::new();
|
||||||
|
|
||||||
for (i, (poll_fd, file)) in poll_fds.iter().zip(files.iter()).enumerate() {
|
for (index, poll_fd) in self.poll_fds.iter().enumerate() {
|
||||||
let Some(file) = file else {
|
let events = if let Some(file) = self.file_at(index) {
|
||||||
continue;
|
file.poll(poll_fd.events(), Some(poller.as_handle_mut()))
|
||||||
};
|
} else {
|
||||||
|
IoEvents::NVAL
|
||||||
|
};
|
||||||
|
|
||||||
let events = file.poll(poll_fd.events(), Some(poller.as_handle_mut()));
|
if events.is_empty() {
|
||||||
if events.is_empty() {
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
poll_fd.revents().set(events);
|
|
||||||
return PollerResult::EventFoundAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
PollerResult::AllRegistered(poller)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Counts the number of the ready files.
|
|
||||||
fn count_all_events(poll_fds: &[PollFd], files: &[Option<Cow<Arc<dyn FileLike>>>]) -> usize {
|
|
||||||
let mut counter = 0;
|
|
||||||
|
|
||||||
for (poll_fd, file) in poll_fds.iter().zip(files.iter()) {
|
|
||||||
let Some(file) = file else {
|
|
||||||
if !poll_fd.revents.get().is_empty() {
|
|
||||||
// This is only possible for POLLNVAL.
|
|
||||||
counter += 1;
|
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let events = file.poll(poll_fd.events(), None);
|
poll_fd.revents().set(events);
|
||||||
if events.is_empty() {
|
return PollerResult::FoundEvents(1 + self.count_events_from(1 + index));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
poll_fd.revents().set(events);
|
PollerResult::Registered(poller)
|
||||||
counter += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
counter
|
/// Counts the number of the ready files.
|
||||||
|
fn count_events(&self) -> usize {
|
||||||
|
self.count_events_from(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Counts the number of the ready files from the given index.
|
||||||
|
fn count_events_from(&self, start: usize) -> usize {
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
for index in start..self.poll_fds.len() {
|
||||||
|
let poll_fd = &self.poll_fds[index];
|
||||||
|
|
||||||
|
let events = if let Some(file) = self.file_at(index) {
|
||||||
|
file.poll(poll_fd.events(), None)
|
||||||
|
} else {
|
||||||
|
IoEvents::NVAL
|
||||||
|
};
|
||||||
|
|
||||||
|
if events.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_fd.revents().set(events);
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
counter
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_at(&self, index: usize) -> Option<&dyn FileLike> {
|
||||||
|
match &self.files {
|
||||||
|
CowFiles::Borrowed(table) => self.poll_fds[index]
|
||||||
|
.fd()
|
||||||
|
.and_then(|fd| table.get_file(fd).ok())
|
||||||
|
.map(Arc::as_ref),
|
||||||
|
CowFiles::Owned(files) => files[index].as_deref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
|
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
|
||||||
|
Reference in New Issue
Block a user