rustc: Switch defaults from libgreen to libnative

The compiler will no longer inject libgreen as the default runtime for rust
programs, this commit switches it over to libnative by default. Now that
libnative has baked for some time, it is ready enough to start getting more
serious usage as the default runtime for rustc generated binaries.

We've found that there isn't really a correct decision in choosing a 1:1 or M:N
runtime as a default for all applications, but it seems that a larger number of
programs today would work more reasonable with a native default rather than a
green default.

With this commit come a number of bugfixes:

* The main native task is now named "<main>"
* The main native task has the stack bounds set up properly
* #[no_uv] was renamed to #[no_start]
* The core-run-destroy test was rewritten for both libnative and libgreen and
  one of the tests was modified to be more robust.
* The process-detach test was locked to libgreen because it uses signal handling
This commit is contained in:
Alex Crichton 2014-03-11 13:38:36 -07:00
parent 7b957a879b
commit ab1dd09d73
15 changed files with 130 additions and 84 deletions

View File

@ -19,6 +19,8 @@ extern crate test;
extern crate getopts;
#[phase(link, syntax)]
extern crate log;
extern crate green;
extern crate rustuv;
use std::os;
use std::io;
@ -41,6 +43,9 @@ pub mod runtest;
pub mod common;
pub mod errors;
#[start]
fn start(argc: int, argv: **u8) -> int { green::start(argc, argv, main) }
pub fn main() {
let args = os::args();
let config = parse_config(args.move_iter().collect());

View File

@ -8,7 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[no_uv];
#[no_uv]; // remove this after stage0
#[allow(attribute_usage)]; // remove this after stage0
extern crate native; // remove this after stage0
#[cfg(rustdoc)]
extern crate this = "rustdoc";
@ -16,7 +18,9 @@ extern crate this = "rustdoc";
#[cfg(rustc)]
extern crate this = "rustc";
extern crate native;
#[cfg(not(stage0))]
fn main() { this::main() }
#[cfg(stage0)]
#[start]
fn start(argc: int, argv: **u8) -> int { native::start(argc, argv, this::main) }

View File

@ -209,7 +209,7 @@ pub mod stack;
pub mod task;
#[lang = "start"]
#[cfg(not(test))]
#[cfg(not(test), stage0)]
pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int {
use std::cast;
start(argc, argv, proc() {

View File

@ -58,6 +58,7 @@
use std::os;
use std::rt;
use std::str;
pub mod io;
pub mod task;
@ -68,6 +69,16 @@ static OS_DEFAULT_STACK_ESTIMATE: uint = 1 << 20;
#[cfg(unix, not(android))]
static OS_DEFAULT_STACK_ESTIMATE: uint = 2 * (1 << 20);
#[lang = "start"]
#[cfg(not(test), not(stage0))]
pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int {
use std::cast;
start(argc, argv, proc() {
let main: extern "Rust" fn() = unsafe { cast::transmute(main) };
main();
})
}
/// Executes the given procedure after initializing the runtime with the given
/// argc/argv.
///
@ -90,7 +101,12 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int {
rt::init(argc, argv);
let mut exit_code = None;
let mut main = Some(main);
let t = task::new((my_stack_bottom, my_stack_top)).run(|| {
let mut task = task::new((my_stack_bottom, my_stack_top));
task.name = Some(str::Slice("<main>"));
let t = task.run(|| {
unsafe {
rt::stack::record_stack_bounds(my_stack_bottom, my_stack_top);
}
exit_code = Some(run(main.take_unwrap()));
});
drop(t);

View File

@ -46,8 +46,8 @@ fn use_std(krate: &ast::Crate) -> bool {
!attr::contains_name(krate.attrs.as_slice(), "no_std")
}
fn use_uv(krate: &ast::Crate) -> bool {
!attr::contains_name(krate.attrs.as_slice(), "no_uv")
fn use_start(krate: &ast::Crate) -> bool {
!attr::contains_name(krate.attrs.as_slice(), "no_start")
}
fn no_prelude(attrs: &[ast::Attribute]) -> bool {
@ -87,18 +87,10 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> {
span: DUMMY_SP
});
if use_uv(&krate) && !self.sess.building_library.get() {
if use_start(&krate) && !self.sess.building_library.get() {
vis.push(ast::ViewItem {
node: ast::ViewItemExternCrate(token::str_to_ident("green"),
with_version("green"),
ast::DUMMY_NODE_ID),
attrs: Vec::new(),
vis: ast::Inherited,
span: DUMMY_SP
});
vis.push(ast::ViewItem {
node: ast::ViewItemExternCrate(token::str_to_ident("rustuv"),
with_version("rustuv"),
node: ast::ViewItemExternCrate(token::str_to_ident("native"),
with_version("native"),
ast::DUMMY_NODE_ID),
attrs: Vec::new(),
vis: ast::Inherited,

View File

@ -961,7 +961,7 @@ fn check_heap_item(cx: &Context, it: &ast::Item) {
}
static crate_attrs: &'static [&'static str] = &[
"crate_type", "feature", "no_uv", "no_main", "no_std", "crate_id",
"crate_type", "feature", "no_start", "no_main", "no_std", "crate_id",
"desc", "comment", "license", "copyright", // not used in rustc now
];

View File

@ -45,6 +45,7 @@ via `close` and `delete` methods.
#[allow(deprecated_owned_vector)]; // NOTE: remove after stage0
#[cfg(test)] extern crate green;
#[cfg(test)] extern crate realrustuv = "rustuv";
use std::cast;
use std::fmt;
@ -69,6 +70,16 @@ pub use self::signal::SignalWatcher;
pub use self::timer::TimerWatcher;
pub use self::tty::TtyWatcher;
// Run tests with libgreen instead of libnative.
//
// FIXME: This egregiously hacks around starting the test runner in a different
// threading mode than the default by reaching into the auto-generated
// '__test' module.
#[cfg(test)] #[start]
fn start(argc: int, argv: **u8) -> int {
green::start(argc, argv, __test::main)
}
mod macros;
mod access;

View File

@ -81,6 +81,16 @@
#[cfg(stage0)]
pub use vec_ng = vec;
// Run tests with libgreen instead of libnative.
//
// FIXME: This egregiously hacks around starting the test runner in a different
// threading mode than the default by reaching into the auto-generated
// '__test' module.
#[cfg(test)] #[start]
fn start(argc: int, argv: **u8) -> int {
green::start(argc, argv, __test::main)
}
pub mod macros;
mod rtdeps;

View File

@ -11,8 +11,6 @@
// ignore-android (FIXME #11419)
// error-pattern:explicit failure
#[no_uv];
extern crate native;
#[start]

View File

@ -10,7 +10,6 @@
#[crate_id="boot#0.1"];
#[crate_type="dylib"];
#[no_uv];
extern crate rustuv;
extern crate green;

View File

@ -10,7 +10,6 @@
#[crate_id="boot#0.1"];
#[crate_type="dylib"];
#[no_uv];
extern crate native;

View File

@ -14,10 +14,9 @@
#[feature(phase)];
#[no_uv];
extern crate native;
#[phase(syntax, link)]
extern crate log;
extern crate native;
use std::fmt;
use std::io::{ChanReader, ChanWriter};

View File

@ -9,19 +9,50 @@
// except according to those terms.
// ignore-fast
// ignore-pretty
// compile-flags:--test
// NB: These tests kill child processes. Valgrind sees these children as leaking
// memory, which makes for some *confusing* logs. That's why these are here
// instead of in std.
use std::io::timer;
use std::libc;
use std::str;
use std::io::process::{Process, ProcessOutput};
#[feature(macro_rules)];
#[test]
fn test_destroy_once() {
extern crate native;
extern crate green;
extern crate rustuv;
macro_rules! iotest (
{ fn $name:ident() $b:block $($a:attr)* } => (
mod $name {
#[allow(unused_imports)];
use std::io::timer;
use std::libc;
use std::str;
use std::io::process::{Process, ProcessOutput};
use native;
use super::*;
fn f() $b
$($a)* #[test] fn green() { f() }
$($a)* #[test] fn native() {
use native;
let (tx, rx) = channel();
native::task::spawn(proc() { tx.send(f()) });
rx.recv();
}
}
)
)
#[cfg(test)] #[start]
fn start(argc: int, argv: **u8) -> int {
green::start(argc, argv, __test::main)
}
iotest!(fn test_destroy_once() {
#[cfg(not(target_os="android"))]
static mut PROG: &'static str = "echo";
@ -30,10 +61,9 @@ fn test_destroy_once() {
let mut p = unsafe {Process::new(PROG, []).unwrap()};
p.signal_exit().unwrap(); // this shouldn't crash (and nor should the destructor)
}
})
#[test]
fn test_destroy_twice() {
iotest!(fn test_destroy_twice() {
#[cfg(not(target_os="android"))]
static mut PROG: &'static str = "echo";
#[cfg(target_os="android")]
@ -45,56 +75,27 @@ fn test_destroy_twice() {
};
p.signal_exit().unwrap(); // this shouldnt crash...
p.signal_exit().unwrap(); // ...and nor should this (and nor should the destructor)
}
})
fn test_destroy_actually_kills(force: bool) {
pub fn test_destroy_actually_kills(force: bool) {
use std::io::process::{Process, ProcessOutput, ExitStatus, ExitSignal};
use std::io::timer;
use std::libc;
use std::str;
#[cfg(unix,not(target_os="android"))]
static mut BLOCK_COMMAND: &'static str = "cat";
static BLOCK_COMMAND: &'static str = "cat";
#[cfg(unix,target_os="android")]
static mut BLOCK_COMMAND: &'static str = "/system/bin/cat";
static BLOCK_COMMAND: &'static str = "/system/bin/cat";
#[cfg(windows)]
static mut BLOCK_COMMAND: &'static str = "cmd";
#[cfg(unix,not(target_os="android"))]
fn process_exists(pid: libc::pid_t) -> bool {
let ProcessOutput {output, ..} = Process::output("ps", [~"-p", pid.to_str()])
.unwrap();
str::from_utf8_owned(output).unwrap().contains(pid.to_str())
}
#[cfg(unix,target_os="android")]
fn process_exists(pid: libc::pid_t) -> bool {
let ProcessOutput {output, ..} = Process::output("/system/bin/ps", [pid.to_str()])
.unwrap();
str::from_utf8_owned(output).unwrap().contains(~"root")
}
#[cfg(windows)]
fn process_exists(pid: libc::pid_t) -> bool {
use std::libc::types::os::arch::extra::DWORD;
use std::libc::funcs::extra::kernel32::{CloseHandle, GetExitCodeProcess, OpenProcess};
use std::libc::consts::os::extra::{FALSE, PROCESS_QUERY_INFORMATION, STILL_ACTIVE };
unsafe {
let process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
if process.is_null() {
return false;
}
// process will be non-null if the process is alive, or if it died recently
let mut status = 0;
GetExitCodeProcess(process, &mut status);
CloseHandle(process);
return status == STILL_ACTIVE;
}
}
static BLOCK_COMMAND: &'static str = "cmd";
// this process will stay alive indefinitely trying to read from stdin
let mut p = unsafe {Process::new(BLOCK_COMMAND, []).unwrap()};
let mut p = Process::new(BLOCK_COMMAND, []).unwrap();
assert!(process_exists(p.id()));
assert!(p.signal(0).is_ok());
if force {
p.signal_kill().unwrap();
@ -102,18 +103,26 @@ fn test_destroy_actually_kills(force: bool) {
p.signal_exit().unwrap();
}
if process_exists(p.id()) {
timer::sleep(500);
assert!(!process_exists(p.id()));
// Don't let this test time out, this should be quick
let (tx, rx1) = channel();
let mut t = timer::Timer::new().unwrap();
let rx2 = t.oneshot(1000);
spawn(proc() {
select! {
() = rx2.recv() => unsafe { libc::exit(1) },
() = rx1.recv() => {}
}
});
match p.wait() {
ExitStatus(..) => fail!("expected a signal"),
ExitSignal(..) => tx.send(()),
}
}
#[test]
fn test_unforced_destroy_actually_kills() {
iotest!(fn test_unforced_destroy_actually_kills() {
test_destroy_actually_kills(false);
}
})
#[test]
fn test_forced_destroy_actually_kills() {
iotest!(fn test_forced_destroy_actually_kills() {
test_destroy_actually_kills(true);
}
})

View File

@ -10,8 +10,6 @@
// ignore-fast
#[no_uv];
#[start]
pub fn main(_: int, _: **u8) -> int {
println!("hello");

View File

@ -20,10 +20,16 @@
// Note that the first thing we do is put ourselves in our own process group so
// we don't interfere with other running tests.
extern crate green;
extern crate rustuv;
use std::libc;
use std::io::process;
use std::io::signal::{Listener, Interrupt};
#[start]
fn start(argc: int, argv: **u8) -> int { green::start(argc, argv, main) }
fn main() {
unsafe { libc::setsid(); }