Implement timeouts
Cleanup utf8 path error handling
This commit is contained in:
@@ -3,10 +3,9 @@ use alloc::boxed::Box;
|
||||
use core::sync::atomic::Ordering;
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
pub use self::context::{Context, Status};
|
||||
pub use self::context::{Context, ContextId, Status};
|
||||
pub use self::list::ContextList;
|
||||
pub use self::switch::switch;
|
||||
pub use context::context::ContextId;
|
||||
|
||||
#[path = "arch/x86_64.rs"]
|
||||
mod arch;
|
||||
@@ -29,6 +28,9 @@ pub mod file;
|
||||
/// Memory struct - contains a set of pages for a context
|
||||
pub mod memory;
|
||||
|
||||
/// Timeout handling
|
||||
pub mod timeout;
|
||||
|
||||
/// Limit on number of contexts
|
||||
pub const CONTEXT_MAX_CONTEXTS: usize = usize::max_value() - 1;
|
||||
|
||||
|
||||
73
src/context/timeout.rs
Normal file
73
src/context/timeout.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use collections::vec_deque::VecDeque;
|
||||
use core::mem;
|
||||
use spin::{Once, Mutex, MutexGuard};
|
||||
|
||||
use context::event;
|
||||
use scheme::SchemeId;
|
||||
use syscall::data::TimeSpec;
|
||||
use syscall::flag::{CLOCK_MONOTONIC, CLOCK_REALTIME, EVENT_READ};
|
||||
use time;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Timeout {
|
||||
pub scheme_id: SchemeId,
|
||||
pub event_id: usize,
|
||||
pub clock: usize,
|
||||
pub time: (u64, u64),
|
||||
}
|
||||
|
||||
type Registry = VecDeque<Timeout>;
|
||||
|
||||
static REGISTRY: Once<Mutex<Registry>> = Once::new();
|
||||
|
||||
/// Initialize registry, called if needed
|
||||
fn init_registry() -> Mutex<Registry> {
|
||||
Mutex::new(Registry::new())
|
||||
}
|
||||
|
||||
/// Get the global timeouts list
|
||||
fn registry() -> MutexGuard<'static, Registry> {
|
||||
REGISTRY.call_once(init_registry).lock()
|
||||
}
|
||||
|
||||
pub fn register(scheme_id: SchemeId, event_id: usize, clock: usize, time: TimeSpec) {
|
||||
let mut registry = registry();
|
||||
registry.push_back(Timeout {
|
||||
scheme_id: scheme_id,
|
||||
event_id: event_id,
|
||||
clock: clock,
|
||||
time: (time.tv_sec as u64, time.tv_nsec as u64)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn trigger() {
|
||||
let mut registry = registry();
|
||||
|
||||
let mono = time::monotonic();
|
||||
let real = time::realtime();
|
||||
|
||||
let mut i = 0;
|
||||
while i < registry.len() {
|
||||
let trigger = match registry[i].clock {
|
||||
CLOCK_MONOTONIC => {
|
||||
let time = registry[i].time;
|
||||
mono.0 > time.0 || (mono.0 == time.0 && mono.1 >= time.1)
|
||||
},
|
||||
CLOCK_REALTIME => {
|
||||
let time = registry[i].time;
|
||||
real.0 > time.0 || (real.0 == time.0 && real.1 >= time.1)
|
||||
},
|
||||
clock => {
|
||||
println!("timeout::trigger: unknown clock {}", clock);
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
if trigger {
|
||||
let timeout = registry.remove(i).unwrap();
|
||||
event::trigger(timeout.scheme_id, timeout.event_id, EVENT_READ, mem::size_of::<TimeSpec>());
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use context::timeout;
|
||||
use device::pic;
|
||||
use device::serial::{COM1, COM2};
|
||||
use time;
|
||||
@@ -36,12 +37,17 @@ interrupt!(pit, {
|
||||
|
||||
const PIT_RATE: u64 = 2250286;
|
||||
|
||||
let mut offset = time::OFFSET.lock();
|
||||
let sum = offset.1 + PIT_RATE;
|
||||
offset.1 = sum % 1000000000;
|
||||
offset.0 += sum / 1000000000;
|
||||
{
|
||||
let mut offset = time::OFFSET.lock();
|
||||
let sum = offset.1 + PIT_RATE;
|
||||
offset.1 = sum % 1000000000;
|
||||
offset.0 += sum / 1000000000;
|
||||
}
|
||||
|
||||
pic::MASTER.ack();
|
||||
|
||||
// Any better way of doing this?
|
||||
timeout::trigger();
|
||||
});
|
||||
|
||||
interrupt!(keyboard, {
|
||||
|
||||
@@ -33,7 +33,7 @@ impl EnvScheme {
|
||||
|
||||
impl Scheme for EnvScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let path = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?.trim_matches('/');
|
||||
let path = str::from_utf8(path).or(Err(Error::new(ENOENT)))?.trim_matches('/');
|
||||
|
||||
let env_lock = {
|
||||
let contexts = context::contexts();
|
||||
|
||||
@@ -42,7 +42,7 @@ impl InitFsScheme {
|
||||
|
||||
impl Scheme for InitFsScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let path_utf8 = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?;
|
||||
let path_utf8 = str::from_utf8(path).or(Err(Error::new(ENOENT)))?;
|
||||
let path_trimmed = path_utf8.trim_matches('/');
|
||||
|
||||
//Have to iterate to get the path without allocation
|
||||
|
||||
@@ -25,6 +25,7 @@ use self::null::NullScheme;
|
||||
use self::pipe::PipeScheme;
|
||||
use self::root::RootScheme;
|
||||
use self::sys::SysScheme;
|
||||
use self::time::TimeScheme;
|
||||
use self::zero::ZeroScheme;
|
||||
|
||||
/// `debug:` - provides access to serial console
|
||||
@@ -61,6 +62,9 @@ pub mod root;
|
||||
/// `sys:` - system information, such as the context list and scheme list
|
||||
pub mod sys;
|
||||
|
||||
/// `time:` - allows reading time, setting timeouts and getting events when they are met
|
||||
pub mod time;
|
||||
|
||||
/// A wrapper around userspace schemes, tightly dependent on `root`
|
||||
pub mod user;
|
||||
|
||||
@@ -114,6 +118,7 @@ impl SchemeList {
|
||||
self.insert(ns, Box::new(*b"memory"), |_| Arc::new(Box::new(MemoryScheme))).unwrap();
|
||||
self.insert(ns, Box::new(*b"null"), |_| Arc::new(Box::new(NullScheme))).unwrap();
|
||||
self.insert(ns, Box::new(*b"sys"), |_| Arc::new(Box::new(SysScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"time"), |scheme_id| Arc::new(Box::new(TimeScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"zero"), |_| Arc::new(Box::new(ZeroScheme))).unwrap();
|
||||
|
||||
ns
|
||||
|
||||
@@ -55,7 +55,7 @@ impl SysScheme {
|
||||
|
||||
impl Scheme for SysScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let path_utf8 = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?;
|
||||
let path_utf8 = str::from_utf8(path).or(Err(Error::new(ENOENT)))?;
|
||||
let path_trimmed = path_utf8.trim_matches('/');
|
||||
|
||||
if path_trimmed.is_empty() {
|
||||
|
||||
113
src/scheme/time.rs
Normal file
113
src/scheme/time.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use collections::BTreeMap;
|
||||
use core::{mem, slice, str};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
use context::timeout;
|
||||
use scheme::SchemeId;
|
||||
use syscall::data::TimeSpec;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC};
|
||||
use syscall::scheme::Scheme;
|
||||
use time;
|
||||
|
||||
pub struct TimeScheme {
|
||||
scheme_id: SchemeId,
|
||||
next_id: AtomicUsize,
|
||||
handles: RwLock<BTreeMap<usize, usize>>
|
||||
}
|
||||
|
||||
impl TimeScheme {
|
||||
pub fn new(scheme_id: SchemeId) -> TimeScheme {
|
||||
TimeScheme {
|
||||
scheme_id: scheme_id,
|
||||
next_id: AtomicUsize::new(0),
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for TimeScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
let clock = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
match clock {
|
||||
CLOCK_REALTIME => (),
|
||||
CLOCK_MONOTONIC => (),
|
||||
_ => return Err(Error::new(ENOENT))
|
||||
}
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, clock);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let new_id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(new_id, clock);
|
||||
Ok(new_id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let time_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut TimeSpec, buf.len()/mem::size_of::<TimeSpec>()) };
|
||||
|
||||
let mut i = 0;
|
||||
while i < time_buf.len() {
|
||||
let arch_time = match clock {
|
||||
CLOCK_REALTIME => time::realtime(),
|
||||
CLOCK_MONOTONIC => time::monotonic(),
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
time_buf[i].tv_sec = arch_time.0 as i64;
|
||||
time_buf[i].tv_nsec = arch_time.1 as i32;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * mem::size_of::<TimeSpec>())
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let time_buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const TimeSpec, buf.len()/mem::size_of::<TimeSpec>()) };
|
||||
|
||||
let mut i = 0;
|
||||
while i < time_buf.len() {
|
||||
let time = time_buf[i];
|
||||
timeout::register(self.scheme_id, id, clock, time);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * mem::size_of::<TimeSpec>())
|
||||
}
|
||||
|
||||
fn fevent(&self, id: usize, _flags: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(id))
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
||||
@@ -5,21 +5,15 @@ use syscall::error::*;
|
||||
use syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC};
|
||||
|
||||
pub fn clock_gettime(clock: usize, time: &mut TimeSpec) -> Result<usize> {
|
||||
match clock {
|
||||
CLOCK_REALTIME => {
|
||||
let arch_time = time::realtime();
|
||||
time.tv_sec = arch_time.0 as i64;
|
||||
time.tv_nsec = arch_time.1 as i32;
|
||||
Ok(0)
|
||||
},
|
||||
CLOCK_MONOTONIC => {
|
||||
let arch_time = time::monotonic();
|
||||
time.tv_sec = arch_time.0 as i64;
|
||||
time.tv_nsec = arch_time.1 as i32;
|
||||
Ok(0)
|
||||
},
|
||||
_ => Err(Error::new(EINVAL))
|
||||
}
|
||||
let arch_time = match clock {
|
||||
CLOCK_REALTIME => time::realtime(),
|
||||
CLOCK_MONOTONIC => time::monotonic(),
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
time.tv_sec = arch_time.0 as i64;
|
||||
time.tv_nsec = arch_time.1 as i32;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result<usize> {
|
||||
|
||||
Reference in New Issue
Block a user