diff --git a/src/context/mod.rs b/src/context/mod.rs index 05b7c580892ede93a39d0f676b63d51eb0f76a0e..20ceedda2a5960788ca3c63a46f9b49e5844fe41 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -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; diff --git a/src/context/timeout.rs b/src/context/timeout.rs new file mode 100644 index 0000000000000000000000000000000000000000..9d9942ae988a8684ff48779454c7cc69f6c762c2 --- /dev/null +++ b/src/context/timeout.rs @@ -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; + } + } +} diff --git a/src/interrupt/irq.rs b/src/interrupt/irq.rs index 976323621a6297d942fd8e0cc21589d51d070a89..0d8fbcb34c348a1509523d591bfd342d61a193c3 100644 --- a/src/interrupt/irq.rs +++ b/src/interrupt/irq.rs @@ -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, { diff --git a/src/scheme/env.rs b/src/scheme/env.rs index 3c88b1e19ea5eddaefe18470ec9b0e3d8720e937..bc8416668c8f059d5c0d2a60d8240b43843ffa46 100644 --- a/src/scheme/env.rs +++ b/src/scheme/env.rs @@ -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(); diff --git a/src/scheme/initfs.rs b/src/scheme/initfs.rs index e3a6a35ef01a72eebbf577e7a8abbe1cdb06ed37..24e58a64c4c404241bda6560ef7af6cd00243117 100644 --- a/src/scheme/initfs.rs +++ b/src/scheme/initfs.rs @@ -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 diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index 8254d114cb19494471ad39f34e2a0f4ae7e0896f..9c0b759cbbb52462f9388b6e697d7af60662a7f6 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -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 diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs index ce5fd23278fde73e6f3239d62812eb6bbf4de058..02273ba45e28235fec715e2754e88e028ac09c08 100644 --- a/src/scheme/sys/mod.rs +++ b/src/scheme/sys/mod.rs @@ -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() { diff --git a/src/scheme/time.rs b/src/scheme/time.rs new file mode 100644 index 0000000000000000000000000000000000000000..3a7fe1bcd5c2118f8753c9af64e286e480743eb2 --- /dev/null +++ b/src/scheme/time.rs @@ -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)) + } +} diff --git a/src/syscall/time.rs b/src/syscall/time.rs index 80c38c5f3f7f3aa00d09681622388b92d224a9e1..bfe82ba9419949ff1d7aca824b9b58d7b7a32232 100644 --- a/src/syscall/time.rs +++ b/src/syscall/time.rs @@ -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> {