feature: simple select()
This commit is contained in:
+46
-8
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user