feature: simple select()
This commit is contained in:
parent
34dbd9d902
commit
496f14d391
@ -87,6 +87,13 @@ fn impl_inode_fn<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"is_ready" => quote! {
|
||||
fn is_ready(&mut self, _node: VnodeRef, _write: bool) ->
|
||||
Result<bool, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
_ => panic!("TODO implement {:?}", name),
|
||||
})
|
||||
}
|
||||
@ -118,6 +125,7 @@ pub fn auto_inode(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
missing.insert("stat".to_string());
|
||||
missing.insert("size".to_string());
|
||||
missing.insert("ioctl".to_string());
|
||||
missing.insert("is_ready".to_string());
|
||||
|
||||
for item in &impl_item.items {
|
||||
match item {
|
||||
|
@ -18,6 +18,8 @@ pub trait CharDevice {
|
||||
|
||||
/// Performs a TTY control request
|
||||
fn ioctl(&self, cmd: IoctlCmd, ptr: usize, lim: usize) -> Result<usize, Errno>;
|
||||
|
||||
fn is_ready(&self, write: bool) -> Result<bool, Errno>;
|
||||
}
|
||||
|
||||
/// Wrapper struct to attach [VnodeImpl] implementation
|
||||
@ -44,6 +46,10 @@ impl VnodeImpl for CharDeviceWrapper {
|
||||
self.device.write(true, data)
|
||||
}
|
||||
|
||||
fn is_ready(&mut self, _node: VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
self.device.is_ready(write)
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
|
@ -118,6 +118,13 @@ impl File {
|
||||
pub fn is_cloexec(&self) -> bool {
|
||||
self.flags & Self::CLOEXEC != 0
|
||||
}
|
||||
|
||||
pub fn is_ready(&self, write: bool) -> Result<bool, Errno> {
|
||||
match &self.inner {
|
||||
FileInner::Normal(inner) => inner.vnode.is_ready(write),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for File {
|
||||
|
@ -80,6 +80,8 @@ pub trait VnodeImpl {
|
||||
/// Reports the size of this filesystem object in bytes
|
||||
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno>;
|
||||
|
||||
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno>;
|
||||
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
@ -398,6 +400,14 @@ impl Vnode {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ready(self: &VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.is_ready(self.clone(), write)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Vnode {
|
||||
|
@ -33,6 +33,10 @@ pub fn derive_tty_char_device(input: TokenStream) -> TokenStream {
|
||||
{
|
||||
crate::dev::tty::TtyDevice::tty_ioctl(self, cmd, ptr, len)
|
||||
}
|
||||
fn is_ready(&self, write: bool) -> Result<bool, libsys::error::Errno> {
|
||||
crate::dev::tty::TtyDevice::is_ready(self, write)
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Teletype (TTY) device facilities
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use crate::proc::wait::Wait;
|
||||
use crate::proc::wait::{Wait, WAIT_SELECT};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use libsys::error::Errno;
|
||||
use libsys::{
|
||||
@ -31,6 +31,17 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
/// Returns a reference to character device's ring buffer
|
||||
fn ring(&self) -> &CharRing<N>;
|
||||
|
||||
fn is_ready(&self, write: bool) -> Result<bool, Errno> {
|
||||
let ring = self.ring();
|
||||
let config = ring.config.lock();
|
||||
|
||||
if write {
|
||||
todo!()
|
||||
} else {
|
||||
Ok(ring.is_readable(config.lflag.contains(TermiosLflag::ICANON)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a TTY control request
|
||||
fn tty_ioctl(&self, cmd: IoctlCmd, ptr: usize, _len: usize) -> Result<usize, Errno> {
|
||||
match cmd {
|
||||
@ -234,6 +245,36 @@ impl<const N: usize> CharRing<N> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_readable(&self, canonical: bool) -> bool {
|
||||
let inner = self.inner.lock();
|
||||
if canonical {
|
||||
// TODO optimize this somehow?
|
||||
let mut rd = inner.rd;
|
||||
let mut count = 0usize;
|
||||
loop {
|
||||
let readable = if rd <= inner.wr {
|
||||
(inner.wr - rd) > 0
|
||||
} else {
|
||||
(inner.wr + (N - rd)) > 0
|
||||
};
|
||||
|
||||
if !readable {
|
||||
break;
|
||||
}
|
||||
|
||||
if inner.data[rd] == b'\n' {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
rd = (rd + 1) % N;
|
||||
}
|
||||
|
||||
count != 0 || inner.flags != 0
|
||||
} else {
|
||||
inner.is_readable() || inner.flags != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a blocking read of a single byte from the buffer
|
||||
pub fn getc(&self) -> Result<u8, Errno> {
|
||||
let mut lock = self.inner.lock();
|
||||
@ -246,15 +287,10 @@ impl<const N: usize> CharRing<N> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if lock.flags != 0 {
|
||||
if lock.flags & (1 << 0) != 0 {
|
||||
lock.flags &= !(1 << 0);
|
||||
return Err(Errno::EndOfFile);
|
||||
}
|
||||
todo!();
|
||||
}
|
||||
let byte = lock.read_unchecked();
|
||||
drop(lock);
|
||||
self.wait_write.wakeup_one();
|
||||
WAIT_SELECT.wakeup_all();
|
||||
Ok(byte)
|
||||
}
|
||||
|
||||
@ -265,7 +301,9 @@ impl<const N: usize> CharRing<N> {
|
||||
todo!()
|
||||
}
|
||||
lock.write_unchecked(ch);
|
||||
drop(lock);
|
||||
self.wait_read.wakeup_one();
|
||||
WAIT_SELECT.wakeup_all();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
use crate::arch::machine;
|
||||
use crate::dev::timer::TimestampSource;
|
||||
use crate::proc::{self, sched::SCHED, Pid, Process};
|
||||
use crate::proc::{self, sched::SCHED, Pid, Process, ProcessRef};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use alloc::collections::LinkedList;
|
||||
use core::time::Duration;
|
||||
use libsys::error::Errno;
|
||||
use libsys::{error::Errno, stat::FdSet};
|
||||
|
||||
/// Wait channel structure. Contains a queue of processes
|
||||
/// waiting for some event to happen.
|
||||
@ -20,6 +20,7 @@ struct Timeout {
|
||||
}
|
||||
|
||||
static TICK_LIST: IrqSafeSpinLock<LinkedList<Timeout>> = IrqSafeSpinLock::new(LinkedList::new());
|
||||
pub static WAIT_SELECT: Wait = Wait::new();
|
||||
|
||||
/// Checks for any timed out wait channels and interrupts them
|
||||
pub fn tick() {
|
||||
@ -54,6 +55,54 @@ pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select(
|
||||
proc: ProcessRef,
|
||||
n: u32,
|
||||
mut rfds: Option<&mut FdSet>,
|
||||
mut wfds: Option<&mut FdSet>,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<u32, Errno> {
|
||||
// TODO support wfds
|
||||
if wfds.is_some() || rfds.is_none() {
|
||||
todo!();
|
||||
}
|
||||
let read = rfds.as_deref().map(FdSet::clone);
|
||||
let write = wfds.as_deref().map(FdSet::clone);
|
||||
rfds.as_deref_mut().map(FdSet::reset);
|
||||
wfds.as_deref_mut().map(FdSet::reset);
|
||||
|
||||
let deadline = timeout.map(|v| v + machine::local_timer().timestamp().unwrap());
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
loop {
|
||||
if let Some(read) = &read {
|
||||
for fd in read.iter() {
|
||||
let file = io.file(fd as usize)?;
|
||||
if file.borrow().is_ready(false)? {
|
||||
rfds.as_mut().unwrap().set(fd);
|
||||
return Ok(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(write) = &write {
|
||||
for fd in write.iter() {
|
||||
let file = io.file(fd as usize)?;
|
||||
if file.borrow().is_ready(true)? {
|
||||
wfds.as_mut().unwrap().set(fd);
|
||||
return Ok(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Suspend
|
||||
match WAIT_SELECT.wait(deadline) {
|
||||
Err(Errno::TimedOut) => return Ok(0),
|
||||
Err(e) => return Err(e),
|
||||
Ok(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Wait {
|
||||
/// Constructs a new wait channel
|
||||
pub const fn new() -> Self {
|
||||
|
@ -18,8 +18,16 @@ fn translate(virt: usize) -> Option<usize> {
|
||||
|
||||
/// Unwraps a slim structure pointer
|
||||
pub fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
|
||||
let bytes = validate_user_ptr(base, size_of::<T>())?;
|
||||
Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
|
||||
validate_user_ptr_struct_option(base).and_then(|e| e.ok_or(Errno::InvalidArgument))
|
||||
}
|
||||
|
||||
pub fn validate_user_ptr_struct_option<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
let bytes = validate_user_ptr(base, size_of::<T>())?;
|
||||
Ok(Some(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) }))
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps an user buffer reference
|
||||
|
@ -3,16 +3,16 @@
|
||||
use crate::arch::platform::exception::ExceptionFrame;
|
||||
use crate::debug::Level;
|
||||
use crate::proc::{elf, wait, Pid, Process, ProcessIo};
|
||||
use core::cmp::Ordering;
|
||||
use core::mem::size_of;
|
||||
use core::ops::DerefMut;
|
||||
use core::time::Duration;
|
||||
use core::cmp::Ordering;
|
||||
use libsys::{
|
||||
abi,
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
signal::Signal,
|
||||
stat::{FileMode, OpenFlags, Stat, AT_EMPTY_PATH, AT_FDCWD},
|
||||
stat::{FdSet, FileMode, OpenFlags, Stat, AT_EMPTY_PATH, AT_FDCWD},
|
||||
traits::{Read, Write},
|
||||
};
|
||||
use vfs::VnodeRef;
|
||||
@ -194,6 +194,22 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
||||
proc.set_signal(signal);
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
abi::SYS_SELECT => {
|
||||
let n = args[0] as u32;
|
||||
let rfds = validate_user_ptr_struct_option::<FdSet>(args[1])?;
|
||||
let wfds = validate_user_ptr_struct_option::<FdSet>(args[2])?;
|
||||
let timeout = if args[3] == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Duration::from_nanos(args[3] as u64))
|
||||
};
|
||||
|
||||
let proc = Process::current();
|
||||
let fd = wait::select(proc, n, rfds, wfds, timeout)?;
|
||||
Ok(fd as usize)
|
||||
}
|
||||
|
||||
_ => {
|
||||
let proc = Process::current();
|
||||
errorln!("Undefined system call: {}", num);
|
||||
|
@ -15,3 +15,4 @@ pub const SYS_FORK: usize = 7;
|
||||
pub const SYS_EXECVE: usize = 8;
|
||||
pub const SYS_WAITPID: usize = 9;
|
||||
pub const SYS_IOCTL: usize = 10;
|
||||
pub const SYS_SELECT: usize = 11;
|
||||
|
@ -2,7 +2,7 @@ use crate::abi;
|
||||
use crate::{
|
||||
ioctl::IoctlCmd,
|
||||
signal::Signal,
|
||||
stat::{FileMode, OpenFlags, Stat},
|
||||
stat::{FdSet, FileMode, OpenFlags, Stat},
|
||||
};
|
||||
|
||||
// TODO document the syscall ABI
|
||||
@ -217,3 +217,19 @@ pub unsafe fn sys_ex_sigreturn() -> ! {
|
||||
pub unsafe fn sys_ex_kill(pid: u32, signum: Signal) -> i32 {
|
||||
syscall!(abi::SYS_EX_KILL, argn!(pid), argn!(signum as u32)) as i32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_select(
|
||||
n: u32,
|
||||
read_fds: Option<&mut FdSet>,
|
||||
write_fds: Option<&mut FdSet>,
|
||||
timeout: u64,
|
||||
) -> i32 {
|
||||
syscall!(
|
||||
abi::SYS_SELECT,
|
||||
argn!(n),
|
||||
argp!(read_fds.map(|e| e as *mut _).unwrap_or(core::ptr::null_mut())),
|
||||
argp!(write_fds.map(|e| e as *mut _).unwrap_or(core::ptr::null_mut())),
|
||||
argn!(timeout)
|
||||
) as i32
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use core::fmt;
|
||||
|
||||
pub const AT_FDCWD: i32 = -2;
|
||||
pub const AT_EMPTY_PATH: u32 = 1 << 16;
|
||||
|
||||
@ -32,6 +34,16 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FdSet {
|
||||
bits: [u64; 2]
|
||||
}
|
||||
|
||||
struct FdSetIter<'a> {
|
||||
idx: u32,
|
||||
set: &'a FdSet
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Stat {
|
||||
@ -40,6 +52,75 @@ pub struct Stat {
|
||||
pub blksize: u32,
|
||||
}
|
||||
|
||||
impl FdSet {
|
||||
pub const fn empty() -> Self {
|
||||
Self { bits: [0; 2] }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset(&mut self) {
|
||||
self.bits.fill(0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.bits.iter().find(|&&x| x != 0).is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set(&mut self, fd: u32) {
|
||||
self.bits[(fd as usize) / 64] |= 1 << (fd % 64);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self, fd: u32) {
|
||||
self.bits[(fd as usize) / 64] &= !(1 << (fd % 64));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set(&self, fd: u32) -> bool {
|
||||
self.bits[(fd as usize) / 64] & (1 << (fd % 64)) != 0
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = u32> + '_ {
|
||||
FdSetIter {
|
||||
idx: 0,
|
||||
set: self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FdSetIter<'_> {
|
||||
type Item = u32;
|
||||
|
||||
fn next(&mut self) -> Option<u32> {
|
||||
while self.idx < 128 {
|
||||
if self.set.is_set(self.idx) {
|
||||
let res = self.idx;
|
||||
self.idx += 1;
|
||||
return Some(res);
|
||||
}
|
||||
self.idx += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FdSet {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "FdSet {{ ")?;
|
||||
let mut count = 0;
|
||||
for fd in self.iter() {
|
||||
if count != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", fd)?;
|
||||
count += 1;
|
||||
}
|
||||
write!(f, " }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl FileMode {
|
||||
/// Returns default permission set for directories
|
||||
pub const fn default_dir() -> Self {
|
||||
|
@ -5,19 +5,37 @@
|
||||
extern crate libusr;
|
||||
|
||||
use libusr::sys::Signal;
|
||||
use libusr::sys::stat::FdSet;
|
||||
|
||||
fn readline(fd: i32, buf: &mut [u8]) -> Result<&str, ()> {
|
||||
let count = unsafe { libusr::sys::sys_read(fd, buf) };
|
||||
if count >= 0 {
|
||||
core::str::from_utf8(&buf[..count as usize]).map_err(|_| ())
|
||||
} else {
|
||||
Err(())
|
||||
// select() just for test
|
||||
loop {
|
||||
let mut rfds = FdSet::empty();
|
||||
rfds.set(fd as u32);
|
||||
let res = unsafe { libusr::sys::sys_select(fd as u32 + 1, Some(&mut rfds), None, 1_000_000_000) };
|
||||
if res < 0 {
|
||||
return Err(());
|
||||
}
|
||||
if res == 0 {
|
||||
continue;
|
||||
}
|
||||
if !rfds.is_set(fd as u32) {
|
||||
panic!();
|
||||
}
|
||||
|
||||
let count = unsafe { libusr::sys::sys_read(fd, buf) };
|
||||
if count >= 0 {
|
||||
return core::str::from_utf8(&buf[..count as usize]).map_err(|_| ());
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let mut buf = [0; 512];
|
||||
|
||||
loop {
|
||||
print!("> ");
|
||||
let line = readline(libusr::sys::STDIN_FILENO, &mut buf).unwrap();
|
||||
|
Loading…
x
Reference in New Issue
Block a user