about summary refs log tree commit diff
path: root/src/libstd/rt/basic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/rt/basic.rs')
-rw-r--r--src/libstd/rt/basic.rs256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/libstd/rt/basic.rs b/src/libstd/rt/basic.rs
new file mode 100644
index 00000000000..86d3f8a52ba
--- /dev/null
+++ b/src/libstd/rt/basic.rs
@@ -0,0 +1,256 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This is a basic event loop implementation not meant for any "real purposes"
+//! other than testing the scheduler and proving that it's possible to have a
+//! pluggable event loop.
+
+use prelude::*;
+
+use cast;
+use rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausibleIdleCallback};
+use unstable::sync::Exclusive;
+use util;
+
+/// This is the only exported function from this module.
+pub fn event_loop() -> ~EventLoop {
+    ~BasicLoop::new() as ~EventLoop
+}
+
+struct BasicLoop {
+    work: ~[~fn()],               // pending work
+    idle: Option<*BasicPausible>, // only one is allowed
+    remotes: ~[(uint, ~fn())],
+    next_remote: uint,
+    messages: Exclusive<~[Message]>
+}
+
+enum Message { RunRemote(uint), RemoveRemote(uint) }
+
+struct Time {
+    sec: u64,
+    nsec: u64,
+}
+
+impl Ord for Time {
+    fn lt(&self, other: &Time) -> bool {
+        self.sec < other.sec || self.nsec < other.nsec
+    }
+}
+
+impl BasicLoop {
+    fn new() -> BasicLoop {
+        BasicLoop {
+            work: ~[],
+            idle: None,
+            next_remote: 0,
+            remotes: ~[],
+            messages: Exclusive::new(~[]),
+        }
+    }
+
+    /// Process everything in the work queue (continually)
+    fn work(&mut self) {
+        while self.work.len() > 0 {
+            for work in util::replace(&mut self.work, ~[]).move_iter() {
+                work();
+            }
+        }
+    }
+
+    fn remote_work(&mut self) {
+        let messages = unsafe {
+            do self.messages.with |messages| {
+                if messages.len() > 0 {
+                    Some(util::replace(messages, ~[]))
+                } else {
+                    None
+                }
+            }
+        };
+        let messages = match messages {
+            Some(m) => m, None => return
+        };
+        for message in messages.iter() {
+            self.message(*message);
+        }
+    }
+
+    fn message(&mut self, message: Message) {
+        match message {
+            RunRemote(i) => {
+                match self.remotes.iter().find(|& &(id, _)| id == i) {
+                    Some(&(_, ref f)) => (*f)(),
+                    None => unreachable!()
+                }
+            }
+            RemoveRemote(i) => {
+                match self.remotes.iter().position(|&(id, _)| id == i) {
+                    Some(i) => { self.remotes.remove(i); }
+                    None => unreachable!()
+                }
+            }
+        }
+    }
+
+    /// Run the idle callback if one is registered
+    fn idle(&mut self) {
+        unsafe {
+            match self.idle {
+                Some(idle) => {
+                    if (*idle).active {
+                        (*(*idle).work.get_ref())();
+                    }
+                }
+                None => {}
+            }
+        }
+    }
+
+    fn has_idle(&self) -> bool {
+        unsafe { self.idle.is_some() && (**self.idle.get_ref()).active }
+    }
+}
+
+impl EventLoop for BasicLoop {
+    fn run(&mut self) {
+        // Not exactly efficient, but it gets the job done.
+        while self.remotes.len() > 0 || self.work.len() > 0 || self.has_idle() {
+
+            self.work();
+            self.remote_work();
+
+            if self.has_idle() {
+                self.idle();
+                continue
+            }
+
+            unsafe {
+                // We block here if we have no messages to process and we may
+                // receive a message at a later date
+                do self.messages.hold_and_wait |messages| {
+                    self.remotes.len() > 0 &&
+                        messages.len() == 0 &&
+                        self.work.len() == 0
+                }
+            }
+        }
+    }
+
+    fn callback(&mut self, f: ~fn()) {
+        self.work.push(f);
+    }
+
+    // XXX: Seems like a really weird requirement to have an event loop provide.
+    fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback {
+        let callback = ~BasicPausible::new(self);
+        rtassert!(self.idle.is_none());
+        unsafe {
+            let cb_ptr: &*BasicPausible = cast::transmute(&callback);
+            self.idle = Some(*cb_ptr);
+        }
+        return callback as ~PausibleIdleCallback;
+    }
+
+    fn remote_callback(&mut self, f: ~fn()) -> ~RemoteCallback {
+        let id = self.next_remote;
+        self.next_remote += 1;
+        self.remotes.push((id, f));
+        ~BasicRemote::new(self.messages.clone(), id) as ~RemoteCallback
+    }
+
+    /// This has no bindings for local I/O
+    fn io<'a>(&'a mut self, _: &fn(&'a mut IoFactory)) {}
+}
+
+struct BasicRemote {
+    queue: Exclusive<~[Message]>,
+    id: uint,
+}
+
+impl BasicRemote {
+    fn new(queue: Exclusive<~[Message]>, id: uint) -> BasicRemote {
+        BasicRemote { queue: queue, id: id }
+    }
+}
+
+impl RemoteCallback for BasicRemote {
+    fn fire(&mut self) {
+        unsafe {
+            do self.queue.hold_and_signal |queue| {
+                queue.push(RunRemote(self.id));
+            }
+        }
+    }
+}
+
+impl Drop for BasicRemote {
+    fn drop(&mut self) {
+        unsafe {
+            do self.queue.hold_and_signal |queue| {
+                queue.push(RemoveRemote(self.id));
+            }
+        }
+    }
+}
+
+struct BasicPausible {
+    eloop: *mut BasicLoop,
+    work: Option<~fn()>,
+    active: bool,
+}
+
+impl BasicPausible {
+    fn new(eloop: &mut BasicLoop) -> BasicPausible {
+        BasicPausible {
+            active: false,
+            work: None,
+            eloop: eloop,
+        }
+    }
+}
+
+impl PausibleIdleCallback for BasicPausible {
+    fn start(&mut self, f: ~fn()) {
+        rtassert!(!self.active && self.work.is_none());
+        self.active = true;
+        self.work = Some(f);
+    }
+    fn pause(&mut self) {
+        self.active = false;
+    }
+    fn resume(&mut self) {
+        self.active = true;
+    }
+    fn close(&mut self) {
+        self.active = false;
+        self.work = None;
+    }
+}
+
+impl Drop for BasicPausible {
+    fn drop(&mut self) {
+        unsafe {
+            (*self.eloop).idle = None;
+        }
+    }
+}
+
+fn time() -> Time {
+    #[fixed_stack_segment]; #[inline(never)];
+    extern {
+        fn get_time(sec: &mut i64, nsec: &mut i32);
+    }
+    let mut sec = 0;
+    let mut nsec = 0;
+    unsafe { get_time(&mut sec, &mut nsec) }
+
+    Time { sec: sec as u64, nsec: nsec as u64 }
+}