feature: simple select()

This commit is contained in:
2021-11-12 16:00:13 +02:00
parent 34dbd9d902
commit 496f14d391
13 changed files with 283 additions and 21 deletions
+46 -8
View File
@@ -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(())
}
}
+51 -2
View File
@@ -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 {
+10 -2
View File
@@ -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
+18 -2
View File
@@ -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);