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