diff options
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/io/test.rs | 1 | ||||
| -rw-r--r-- | src/libstd/io/timer.rs | 174 | ||||
| -rw-r--r-- | src/libstd/libc.rs | 1 | ||||
| -rw-r--r-- | src/libstd/rt/at_exit_imp.rs | 72 | ||||
| -rw-r--r-- | src/libstd/rt/mod.rs | 22 |
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(); } |
