Implement timeouts

Cleanup utf8 path error handling
This commit is contained in:
Jeremy Soller
2017-04-08 21:59:30 -06:00
parent b286e69c9d
commit e43f5dda81
9 changed files with 217 additions and 24 deletions

View File

@@ -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
View 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;
}
}
}

View File

@@ -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, {

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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))
}
}

View File

@@ -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> {