auto merge of #12833 : alexcrichton/rust/libnative, r=brson
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 reasonably 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:
commit
bbf8cdc43f
17 changed files with 284 additions and 243 deletions
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -11,124 +11,178 @@
|
|||
//! Interfaces to the operating system provided random number
|
||||
//! generators.
|
||||
|
||||
use Rng;
|
||||
pub use self::imp::OSRng;
|
||||
|
||||
#[cfg(unix)]
|
||||
use reader::ReaderRng;
|
||||
#[cfg(unix)]
|
||||
use std::io::File;
|
||||
mod imp {
|
||||
use Rng;
|
||||
use reader::ReaderRng;
|
||||
use std::io::File;
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::cast;
|
||||
#[cfg(windows)]
|
||||
use std::libc::{c_long, DWORD, BYTE};
|
||||
#[cfg(windows)]
|
||||
type HCRYPTPROV = c_long;
|
||||
// the extern functions imported from the runtime on Windows are
|
||||
// implemented so that they either succeed or abort(), so we can just
|
||||
// assume they work when we call them.
|
||||
|
||||
/// A random number generator that retrieves randomness straight from
|
||||
/// the operating system. Platform sources:
|
||||
///
|
||||
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
|
||||
/// `/dev/urandom`.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
///
|
||||
/// This does not block.
|
||||
#[cfg(unix)]
|
||||
pub struct OSRng {
|
||||
priv inner: ReaderRng<File>
|
||||
}
|
||||
/// A random number generator that retrieves randomness straight from
|
||||
/// the operating system. Platform sources:
|
||||
///
|
||||
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
|
||||
/// `/dev/urandom`.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
///
|
||||
/// This does not block.
|
||||
#[cfg(windows)]
|
||||
pub struct OSRng {
|
||||
priv hcryptprov: HCRYPTPROV
|
||||
}
|
||||
|
||||
impl OSRng {
|
||||
/// Create a new `OSRng`.
|
||||
/// A random number generator that retrieves randomness straight from
|
||||
/// the operating system. Platform sources:
|
||||
///
|
||||
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
|
||||
/// `/dev/urandom`.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
///
|
||||
/// This does not block.
|
||||
#[cfg(unix)]
|
||||
pub fn new() -> OSRng {
|
||||
let reader = File::open(&Path::new("/dev/urandom"));
|
||||
let reader = reader.ok().expect("Error opening /dev/urandom");
|
||||
let reader_rng = ReaderRng::new(reader);
|
||||
|
||||
OSRng { inner: reader_rng }
|
||||
pub struct OSRng {
|
||||
priv inner: ReaderRng<File>
|
||||
}
|
||||
|
||||
/// Create a new `OSRng`.
|
||||
#[cfg(windows)]
|
||||
pub fn new() -> OSRng {
|
||||
extern { fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV); }
|
||||
impl OSRng {
|
||||
/// Create a new `OSRng`.
|
||||
pub fn new() -> OSRng {
|
||||
let reader = File::open(&Path::new("/dev/urandom"));
|
||||
let reader = reader.ok().expect("Error opening /dev/urandom");
|
||||
let reader_rng = ReaderRng::new(reader);
|
||||
|
||||
let mut hcp = 0;
|
||||
unsafe {rust_win32_rand_acquire(&mut hcp)};
|
||||
|
||||
OSRng { hcryptprov: hcp }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl Rng for OSRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.inner.next_u32()
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.inner.next_u64()
|
||||
}
|
||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||
self.inner.fill_bytes(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl Rng for OSRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
let mut v = [0u8, .. 4];
|
||||
self.fill_bytes(v);
|
||||
unsafe { cast::transmute(v) }
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
let mut v = [0u8, .. 8];
|
||||
self.fill_bytes(v);
|
||||
unsafe { cast::transmute(v) }
|
||||
}
|
||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||
extern {
|
||||
fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD,
|
||||
pbBuffer: *mut BYTE);
|
||||
OSRng { inner: reader_rng }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {rust_win32_rand_gen(self.hcryptprov, v.len() as DWORD, v.as_mut_ptr())}
|
||||
impl Rng for OSRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.inner.next_u32()
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.inner.next_u64()
|
||||
}
|
||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||
self.inner.fill_bytes(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OSRng {
|
||||
#[cfg(unix)]
|
||||
fn drop(&mut self) {
|
||||
// ensure that OSRng is not implicitly copyable on all
|
||||
// platforms, for consistency.
|
||||
#[cfg(windows)]
|
||||
mod imp {
|
||||
use Rng;
|
||||
use std::cast;
|
||||
use std::libc::{c_ulong, DWORD, BYTE, LPCSTR, BOOL};
|
||||
use std::os;
|
||||
use std::rt::stack;
|
||||
|
||||
type HCRYPTPROV = c_ulong;
|
||||
|
||||
/// A random number generator that retrieves randomness straight from
|
||||
/// the operating system. Platform sources:
|
||||
///
|
||||
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
|
||||
/// `/dev/urandom`.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
///
|
||||
/// This does not block.
|
||||
pub struct OSRng {
|
||||
priv hcryptprov: HCRYPTPROV
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn drop(&mut self) {
|
||||
extern { fn rust_win32_rand_release(hProv: HCRYPTPROV); }
|
||||
static PROV_RSA_FULL: DWORD = 1;
|
||||
static CRYPT_SILENT: DWORD = 64;
|
||||
static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
|
||||
static NTE_BAD_SIGNATURE: DWORD = 0x80090006;
|
||||
|
||||
unsafe {rust_win32_rand_release(self.hcryptprov)}
|
||||
extern "system" {
|
||||
fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
|
||||
pszContainer: LPCSTR,
|
||||
pszProvider: LPCSTR,
|
||||
dwProvType: DWORD,
|
||||
dwFlags: DWORD) -> BOOL;
|
||||
fn CryptGenRandom(hProv: HCRYPTPROV,
|
||||
dwLen: DWORD,
|
||||
pbBuffer: *mut BYTE) -> BOOL;
|
||||
fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
|
||||
}
|
||||
|
||||
impl OSRng {
|
||||
/// Create a new `OSRng`.
|
||||
pub fn new() -> OSRng {
|
||||
let mut hcp = 0;
|
||||
let mut ret = unsafe {
|
||||
CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
|
||||
PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
|
||||
};
|
||||
|
||||
// It turns out that if we can't acquire a context with the
|
||||
// NTE_BAD_SIGNATURE error code, the documentation states:
|
||||
//
|
||||
// The provider DLL signature could not be verified. Either the
|
||||
// DLL or the digital signature has been tampered with.
|
||||
//
|
||||
// Sounds fishy, no? As it turns out, our signature can be bad
|
||||
// because our Thread Information Block (TIB) isn't exactly what it
|
||||
// expects. As to why, I have no idea. The only data we store in the
|
||||
// TIB is the stack limit for each thread, but apparently that's
|
||||
// enough to make the signature valid.
|
||||
//
|
||||
// Furthermore, this error only happens the *first* time we call
|
||||
// CryptAcquireContext, so we don't have to worry about future
|
||||
// calls.
|
||||
//
|
||||
// Anyway, the fix employed here is that if we see this error, we
|
||||
// pray that we're not close to the end of the stack, temporarily
|
||||
// set the stack limit to 0 (what the TIB originally was), acquire a
|
||||
// context, and then reset the stack limit.
|
||||
//
|
||||
// Again, I'm not sure why this is the fix, nor why we're getting
|
||||
// this error. All I can say is that this seems to allow libnative
|
||||
// to progress where it otherwise would be hindered. Who knew?
|
||||
if ret == 0 && os::errno() as DWORD == NTE_BAD_SIGNATURE {
|
||||
unsafe {
|
||||
let limit = stack::get_sp_limit();
|
||||
stack::record_sp_limit(0);
|
||||
ret = CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
|
||||
PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
|
||||
stack::record_sp_limit(limit);
|
||||
}
|
||||
}
|
||||
|
||||
if ret == 0 {
|
||||
fail!("couldn't create context: {}", os::last_os_error());
|
||||
}
|
||||
OSRng { hcryptprov: hcp }
|
||||
}
|
||||
}
|
||||
|
||||
impl Rng for OSRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
let mut v = [0u8, .. 4];
|
||||
self.fill_bytes(v);
|
||||
unsafe { cast::transmute(v) }
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
let mut v = [0u8, .. 8];
|
||||
self.fill_bytes(v);
|
||||
unsafe { cast::transmute(v) }
|
||||
}
|
||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||
let ret = unsafe {
|
||||
CryptGenRandom(self.hcryptprov, v.len() as DWORD,
|
||||
v.as_mut_ptr())
|
||||
};
|
||||
if ret == 0 {
|
||||
fail!("couldn't generate random bytes: {}", os::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OSRng {
|
||||
fn drop(&mut self) {
|
||||
let ret = unsafe {
|
||||
CryptReleaseContext(self.hcryptprov, 0)
|
||||
};
|
||||
if ret == 0 {
|
||||
fail!("couldn't release context: {}", os::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::OSRng;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -387,65 +387,6 @@ rust_unset_sigprocmask() {
|
|||
|
||||
#endif
|
||||
|
||||
#if defined(__WIN32__)
|
||||
void
|
||||
win32_require(LPCTSTR fn, BOOL ok) {
|
||||
if (!ok) {
|
||||
LPTSTR buf;
|
||||
DWORD err = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &buf, 0, NULL );
|
||||
fprintf(stderr, "%s failed with error %ld: %s", fn, err, buf);
|
||||
LocalFree((HLOCAL)buf);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rust_win32_rand_acquire(HCRYPTPROV* phProv) {
|
||||
win32_require
|
||||
(_T("CryptAcquireContext"),
|
||||
// changes to the parameters here should be reflected in the docs of
|
||||
// rand::os::OSRng
|
||||
CryptAcquireContext(phProv, NULL, NULL, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
|
||||
|
||||
}
|
||||
void
|
||||
rust_win32_rand_gen(HCRYPTPROV hProv, DWORD dwLen, BYTE* pbBuffer) {
|
||||
win32_require
|
||||
(_T("CryptGenRandom"), CryptGenRandom(hProv, dwLen, pbBuffer));
|
||||
}
|
||||
void
|
||||
rust_win32_rand_release(HCRYPTPROV hProv) {
|
||||
win32_require
|
||||
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// these symbols are listed in rustrt.def.in, so they need to exist; but they
|
||||
// should never be called.
|
||||
|
||||
void
|
||||
rust_win32_rand_acquire() {
|
||||
abort();
|
||||
}
|
||||
void
|
||||
rust_win32_rand_gen() {
|
||||
abort();
|
||||
}
|
||||
void
|
||||
rust_win32_rand_release() {
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
// ignore-android (FIXME #11419)
|
||||
// error-pattern:explicit failure
|
||||
|
||||
#[no_uv];
|
||||
|
||||
extern crate native;
|
||||
|
||||
#[start]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#[crate_id="boot#0.1"];
|
||||
#[crate_type="dylib"];
|
||||
#[no_uv];
|
||||
|
||||
extern crate rustuv;
|
||||
extern crate green;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#[crate_id="boot#0.1"];
|
||||
#[crate_type="dylib"];
|
||||
#[no_uv];
|
||||
|
||||
extern crate native;
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
// ignore-fast
|
||||
|
||||
#[no_uv];
|
||||
|
||||
#[start]
|
||||
pub fn main(_: int, _: **u8) -> int {
|
||||
println!("hello");
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue