about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-01-22 20:41:29 -0800
committerbors <bors@rust-lang.org>2014-01-22 20:41:29 -0800
commit7ea063ea0f9a28920fafbbf21064f20eb7ca02a8 (patch)
tree429ddb0b6f5287ee3dbe962261f68c09d910db42 /src/libstd
parentfce792249e72a181f2ad52413b25b1db643c371f (diff)
parentb8e43838cf7e97b81bf2f7ce6e3d1f8a05c166f5 (diff)
downloadrust-7ea063ea0f9a28920fafbbf21064f20eb7ca02a8.tar.gz
rust-7ea063ea0f9a28920fafbbf21064f20eb7ca02a8.zip
auto merge of #11294 : alexcrichton/rust/native-timer, r=brson
Commit messages have the fun details

Closes #10925
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/io/test.rs1
-rw-r--r--src/libstd/io/timer.rs174
-rw-r--r--src/libstd/libc.rs1
-rw-r--r--src/libstd/rt/at_exit_imp.rs72
-rw-r--r--src/libstd/rt/mod.rs22
5 files changed, 241 insertions, 29 deletions
diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs
index 92b2cfa8be2..d81de989df7 100644
--- a/src/libstd/io/test.rs
+++ b/src/libstd/io/test.rs
@@ -34,6 +34,7 @@ macro_rules! iotest (
             use io::net::udp::*;
             #[cfg(unix)]
             use io::net::unix::*;
+            use io::timer::*;
             use io::process::*;
             use str;
             use util;
diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs
index d156a7460e1..4bf89a1d559 100644
--- a/src/libstd/io/timer.rs
+++ b/src/libstd/io/timer.rs
@@ -96,61 +96,177 @@ impl Timer {
 
 #[cfg(test)]
 mod test {
-    use prelude::*;
-    use super::*;
-
-    #[test]
-    fn test_io_timer_sleep_simple() {
+    iotest!(fn test_io_timer_sleep_simple() {
         let mut timer = Timer::new().unwrap();
         timer.sleep(1);
-    }
+    })
 
-    #[test]
-    fn test_io_timer_sleep_oneshot() {
+    iotest!(fn test_io_timer_sleep_oneshot() {
         let mut timer = Timer::new().unwrap();
         timer.oneshot(1).recv();
-    }
+    })
 
-    #[test]
-    fn test_io_timer_sleep_oneshot_forget() {
+    iotest!(fn test_io_timer_sleep_oneshot_forget() {
         let mut timer = Timer::new().unwrap();
         timer.oneshot(100000000000);
-    }
+    })
 
-    #[test]
-    fn oneshot_twice() {
+    iotest!(fn oneshot_twice() {
         let mut timer = Timer::new().unwrap();
         let port1 = timer.oneshot(10000);
         let port = timer.oneshot(1);
         port.recv();
-        assert!(port1.recv_opt().is_none());
-    }
+        assert_eq!(port1.recv_opt(), None);
+    })
 
-    #[test]
-    fn test_io_timer_oneshot_then_sleep() {
+    iotest!(fn test_io_timer_oneshot_then_sleep() {
         let mut timer = Timer::new().unwrap();
         let port = timer.oneshot(100000000000);
         timer.sleep(1); // this should invalidate the port
-        assert!(port.recv_opt().is_none());
-    }
 
-    #[test]
-    fn test_io_timer_sleep_periodic() {
+        assert_eq!(port.recv_opt(), None);
+    })
+
+    iotest!(fn test_io_timer_sleep_periodic() {
         let mut timer = Timer::new().unwrap();
         let port = timer.periodic(1);
         port.recv();
         port.recv();
         port.recv();
-    }
+    })
 
-    #[test]
-    fn test_io_timer_sleep_periodic_forget() {
+    iotest!(fn test_io_timer_sleep_periodic_forget() {
         let mut timer = Timer::new().unwrap();
         timer.periodic(100000000000);
-    }
+    })
 
-    #[test]
-    fn test_io_timer_sleep_standalone() {
+    iotest!(fn test_io_timer_sleep_standalone() {
         sleep(1)
-    }
+    })
+
+    iotest!(fn oneshot() {
+        let mut timer = Timer::new().unwrap();
+
+        let port = timer.oneshot(1);
+        port.recv();
+        assert!(port.recv_opt().is_none());
+
+        let port = timer.oneshot(1);
+        port.recv();
+        assert!(port.recv_opt().is_none());
+    })
+
+    iotest!(fn override() {
+        let mut timer = Timer::new().unwrap();
+        let oport = timer.oneshot(100);
+        let pport = timer.periodic(100);
+        timer.sleep(1);
+        assert_eq!(oport.recv_opt(), None);
+        assert_eq!(pport.recv_opt(), None);
+        timer.oneshot(1).recv();
+    })
+
+    iotest!(fn period() {
+        let mut timer = Timer::new().unwrap();
+        let port = timer.periodic(1);
+        port.recv();
+        port.recv();
+        let port2 = timer.periodic(1);
+        port2.recv();
+        port2.recv();
+    })
+
+    iotest!(fn sleep() {
+        let mut timer = Timer::new().unwrap();
+        timer.sleep(1);
+        timer.sleep(1);
+    })
+
+    iotest!(fn oneshot_fail() {
+        let mut timer = Timer::new().unwrap();
+        let _port = timer.oneshot(1);
+        fail!();
+    } #[should_fail])
+
+    iotest!(fn period_fail() {
+        let mut timer = Timer::new().unwrap();
+        let _port = timer.periodic(1);
+        fail!();
+    } #[should_fail])
+
+    iotest!(fn normal_fail() {
+        let _timer = Timer::new().unwrap();
+        fail!();
+    } #[should_fail])
+
+    iotest!(fn closing_channel_during_drop_doesnt_kill_everything() {
+        // see issue #10375
+        let mut timer = Timer::new().unwrap();
+        let timer_port = timer.periodic(1000);
+
+        do spawn {
+            timer_port.recv_opt();
+        }
+
+        // when we drop the TimerWatcher we're going to destroy the channel,
+        // which must wake up the task on the other end
+    })
+
+    iotest!(fn reset_doesnt_switch_tasks() {
+        // similar test to the one above.
+        let mut timer = Timer::new().unwrap();
+        let timer_port = timer.periodic(1000);
+
+        do spawn {
+            timer_port.recv_opt();
+        }
+
+        timer.oneshot(1);
+    })
+
+    iotest!(fn reset_doesnt_switch_tasks2() {
+        // similar test to the one above.
+        let mut timer = Timer::new().unwrap();
+        let timer_port = timer.periodic(1000);
+
+        do spawn {
+            timer_port.recv_opt();
+        }
+
+        timer.sleep(1);
+    })
+
+    iotest!(fn sender_goes_away_oneshot() {
+        let port = {
+            let mut timer = Timer::new().unwrap();
+            timer.oneshot(1000)
+        };
+        assert_eq!(port.recv_opt(), None);
+    })
+
+    iotest!(fn sender_goes_away_period() {
+        let port = {
+            let mut timer = Timer::new().unwrap();
+            timer.periodic(1000)
+        };
+        assert_eq!(port.recv_opt(), None);
+    })
+
+    iotest!(fn receiver_goes_away_oneshot() {
+        let mut timer1 = Timer::new().unwrap();
+        timer1.oneshot(1);
+        let mut timer2 = Timer::new().unwrap();
+        // while sleeping, the prevous timer should fire and not have its
+        // callback do something terrible.
+        timer2.sleep(2);
+    })
+
+    iotest!(fn receiver_goes_away_period() {
+        let mut timer1 = Timer::new().unwrap();
+        timer1.periodic(1);
+        let mut timer2 = Timer::new().unwrap();
+        // while sleeping, the prevous timer should fire and not have its
+        // callback do something terrible.
+        timer2.sleep(2);
+    })
 }
diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs
index 77ac226a7f1..8975c2a7955 100644
--- a/src/libstd/libc.rs
+++ b/src/libstd/libc.rs
@@ -3548,6 +3548,7 @@ pub mod funcs {
                 pub fn setsid() -> pid_t;
                 pub fn setuid(uid: uid_t) -> c_int;
                 pub fn sleep(secs: c_uint) -> c_uint;
+                pub fn usleep(secs: c_uint) -> c_int;
                 pub fn sysconf(name: c_int) -> c_long;
                 pub fn tcgetpgrp(fd: c_int) -> pid_t;
                 pub fn ttyname(fd: c_int) -> *c_char;
diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs
new file mode 100644
index 00000000000..6f9be64a73d
--- /dev/null
+++ b/src/libstd/rt/at_exit_imp.rs
@@ -0,0 +1,72 @@
+// 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.
+
+//! Implementation of running at_exit routines
+//!
+//! Documentation can be found on the `rt::at_exit` function.
+
+use cast;
+use iter::Iterator;
+use option::{Some, None};
+use ptr::RawPtr;
+use unstable::sync::Exclusive;
+use util;
+use vec::OwnedVector;
+
+type Queue = Exclusive<~[proc()]>;
+
+// You'll note that these variables are *not* atomic, and this is done on
+// purpose. This module is designed to have init() called *once* in a
+// single-task context, and then run() is called only once in another
+// single-task context. As a result of this, only the `push` function is
+// thread-safe, and it assumes that the `init` function has run previously.
+static mut QUEUE: *mut Queue = 0 as *mut Queue;
+static mut RUNNING: bool = false;
+
+pub fn init() {
+    unsafe {
+        rtassert!(!RUNNING);
+        rtassert!(QUEUE.is_null());
+        let state: ~Queue = ~Exclusive::new(~[]);
+        QUEUE = cast::transmute(state);
+    }
+}
+
+pub fn push(f: proc()) {
+    unsafe {
+        rtassert!(!RUNNING);
+        rtassert!(!QUEUE.is_null());
+        let state: &mut Queue = cast::transmute(QUEUE);
+        let mut f = Some(f);
+        state.with(|arr|  {
+            arr.push(f.take_unwrap());
+        });
+    }
+}
+
+pub fn run() {
+    let vec = unsafe {
+        rtassert!(!RUNNING);
+        rtassert!(!QUEUE.is_null());
+        RUNNING = true;
+        let state: ~Queue = cast::transmute(QUEUE);
+        QUEUE = 0 as *mut Queue;
+        let mut vec = None;
+        state.with(|arr| {
+            vec = Some(util::replace(arr, ~[]));
+        });
+        vec.take_unwrap()
+    };
+
+
+    for f in vec.move_iter() {
+        f();
+    }
+}
diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs
index 40e9a3ec5b2..7aa966802f2 100644
--- a/src/libstd/rt/mod.rs
+++ b/src/libstd/rt/mod.rs
@@ -127,6 +127,9 @@ mod util;
 // Global command line argument storage
 pub mod args;
 
+// Support for running procedures when a program has exited.
+mod at_exit_imp;
+
 /// The default error code of the rust runtime if the main task fails instead
 /// of exiting cleanly.
 pub static DEFAULT_ERROR_CODE: int = 101;
@@ -171,9 +174,27 @@ pub fn init(argc: int, argv: **u8) {
         env::init();
         logging::init();
         local_ptr::init();
+        at_exit_imp::init();
     }
 }
 
+/// Enqueues a procedure to run when the runtime is cleaned up
+///
+/// The procedure passed to this function will be executed as part of the
+/// runtime cleanup phase. For normal rust programs, this means that it will run
+/// after all other tasks have exited.
+///
+/// The procedure is *not* executed with a local `Task` available to it, so
+/// primitives like logging, I/O, channels, spawning, etc, are *not* available.
+/// This is meant for "bare bones" usage to clean up runtime details, this is
+/// not meant as a general-purpose "let's clean everything up" function.
+///
+/// It is forbidden for procedures to register more `at_exit` handlers when they
+/// are running, and doing so will lead to a process abort.
+pub fn at_exit(f: proc()) {
+    at_exit_imp::push(f);
+}
+
 /// One-time runtime cleanup.
 ///
 /// This function is unsafe because it performs no checks to ensure that the
@@ -184,6 +205,7 @@ pub fn init(argc: int, argv: **u8) {
 /// Invoking cleanup while portions of the runtime are still in use may cause
 /// undefined behavior.
 pub unsafe fn cleanup() {
+    at_exit_imp::run();
     args::cleanup();
     local_ptr::cleanup();
 }