2013-04-20 00:33:49 -07:00
|
|
|
// 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.
|
|
|
|
|
2013-07-08 14:41:07 -07:00
|
|
|
use libc;
|
2013-05-15 17:20:48 -07:00
|
|
|
use uint;
|
2013-05-30 13:20:17 -07:00
|
|
|
use option::{Some, None};
|
2013-04-23 15:11:28 -07:00
|
|
|
use cell::Cell;
|
2013-05-28 23:35:22 -07:00
|
|
|
use clone::Clone;
|
|
|
|
use container::Container;
|
2013-08-01 03:16:42 -04:00
|
|
|
use iterator::Iterator;
|
2013-06-16 02:03:37 -07:00
|
|
|
use vec::{OwnedVector, MutableVector};
|
2013-07-02 16:40:57 -07:00
|
|
|
use super::io::net::ip::{IpAddr, Ipv4, Ipv6};
|
2013-06-26 16:41:00 -07:00
|
|
|
use rt::sched::Scheduler;
|
|
|
|
use unstable::run_in_bare_thread;
|
|
|
|
use rt::thread::Thread;
|
|
|
|
use rt::task::Task;
|
|
|
|
use rt::uv::uvio::UvEventLoop;
|
2013-05-28 23:35:22 -07:00
|
|
|
use rt::work_queue::WorkQueue;
|
2013-06-26 16:41:00 -07:00
|
|
|
use rt::sleeper_list::SleeperList;
|
|
|
|
use rt::comm::oneshot;
|
|
|
|
use result::{Result, Ok, Err};
|
2013-05-22 22:18:29 -07:00
|
|
|
|
|
|
|
pub fn new_test_uv_sched() -> Scheduler {
|
|
|
|
|
2013-06-26 16:41:00 -07:00
|
|
|
let mut sched = Scheduler::new(~UvEventLoop::new(),
|
|
|
|
WorkQueue::new(),
|
|
|
|
SleeperList::new());
|
2013-07-19 14:25:05 -07:00
|
|
|
|
2013-05-29 15:55:23 -07:00
|
|
|
// Don't wait for the Shutdown message
|
|
|
|
sched.no_sleep = true;
|
|
|
|
return sched;
|
2013-07-19 14:25:05 -07:00
|
|
|
|
2013-05-22 22:18:29 -07:00
|
|
|
}
|
2013-04-20 02:41:30 -07:00
|
|
|
|
2013-04-20 00:33:49 -07:00
|
|
|
pub fn run_in_newsched_task(f: ~fn()) {
|
2013-06-04 12:03:58 +02:00
|
|
|
let f = Cell::new(f);
|
2013-04-20 00:33:49 -07:00
|
|
|
do run_in_bare_thread {
|
2013-07-19 14:25:05 -07:00
|
|
|
run_in_newsched_task_core(f.take());
|
2013-04-20 00:33:49 -07:00
|
|
|
}
|
|
|
|
}
|
2013-04-20 01:16:06 -07:00
|
|
|
|
2013-07-19 14:25:05 -07:00
|
|
|
pub fn run_in_newsched_task_core(f: ~fn()) {
|
|
|
|
|
|
|
|
use rt::sched::Shutdown;
|
|
|
|
|
|
|
|
let mut sched = ~new_test_uv_sched();
|
|
|
|
let exit_handle = Cell::new(sched.make_handle());
|
|
|
|
|
|
|
|
let on_exit: ~fn(bool) = |exit_status| {
|
|
|
|
exit_handle.take().send(Shutdown);
|
|
|
|
rtassert!(exit_status);
|
|
|
|
};
|
|
|
|
let mut task = ~Task::new_root(&mut sched.stack_pool, f);
|
|
|
|
task.death.on_exit = Some(on_exit);
|
|
|
|
|
|
|
|
sched.bootstrap(task);
|
|
|
|
}
|
|
|
|
|
2013-05-28 23:35:22 -07:00
|
|
|
/// Create more than one scheduler and run a function in a task
|
|
|
|
/// in one of the schedulers. The schedulers will stay alive
|
|
|
|
/// until the function `f` returns.
|
|
|
|
pub fn run_in_mt_newsched_task(f: ~fn()) {
|
2013-06-05 22:35:23 -07:00
|
|
|
use os;
|
|
|
|
use from_str::FromStr;
|
2013-05-29 15:55:23 -07:00
|
|
|
use rt::sched::Shutdown;
|
2013-05-28 23:35:22 -07:00
|
|
|
|
2013-07-19 14:25:05 -07:00
|
|
|
let f = Cell::new(f);
|
2013-05-28 23:35:22 -07:00
|
|
|
|
|
|
|
do run_in_bare_thread {
|
2013-07-17 10:47:05 -07:00
|
|
|
let nthreads = match os::getenv("RUST_RT_TEST_THREADS") {
|
2013-06-05 22:35:23 -07:00
|
|
|
Some(nstr) => FromStr::from_str(nstr).get(),
|
2013-06-23 14:15:39 -07:00
|
|
|
None => {
|
2013-07-17 10:51:54 -07:00
|
|
|
// A reasonable number of threads for testing
|
|
|
|
// multithreading. NB: It's easy to exhaust OS X's
|
|
|
|
// low maximum fd limit by setting this too high (#7772)
|
|
|
|
4
|
2013-06-05 22:35:23 -07:00
|
|
|
}
|
|
|
|
};
|
2013-05-28 23:35:22 -07:00
|
|
|
|
|
|
|
let sleepers = SleeperList::new();
|
|
|
|
let work_queue = WorkQueue::new();
|
|
|
|
|
|
|
|
let mut handles = ~[];
|
|
|
|
let mut scheds = ~[];
|
|
|
|
|
2013-06-05 22:35:23 -07:00
|
|
|
for uint::range(0, nthreads) |_| {
|
2013-05-28 23:35:22 -07:00
|
|
|
let loop_ = ~UvEventLoop::new();
|
2013-06-26 16:41:00 -07:00
|
|
|
let mut sched = ~Scheduler::new(loop_,
|
|
|
|
work_queue.clone(),
|
|
|
|
sleepers.clone());
|
2013-05-28 23:35:22 -07:00
|
|
|
let handle = sched.make_handle();
|
2013-06-12 11:32:22 -07:00
|
|
|
|
2013-05-28 23:35:22 -07:00
|
|
|
handles.push(handle);
|
|
|
|
scheds.push(sched);
|
|
|
|
}
|
|
|
|
|
2013-06-16 02:03:37 -07:00
|
|
|
let handles = Cell::new(handles);
|
2013-06-13 23:31:19 -07:00
|
|
|
let on_exit: ~fn(bool) = |exit_status| {
|
2013-05-29 15:55:23 -07:00
|
|
|
let mut handles = handles.take();
|
|
|
|
// Tell schedulers to exit
|
2013-08-01 03:16:42 -04:00
|
|
|
foreach handle in handles.mut_iter() {
|
2013-05-29 15:55:23 -07:00
|
|
|
handle.send(Shutdown);
|
|
|
|
}
|
2013-05-28 23:35:22 -07:00
|
|
|
|
2013-06-13 23:31:19 -07:00
|
|
|
rtassert!(exit_status);
|
|
|
|
};
|
2013-06-26 16:41:00 -07:00
|
|
|
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
|
2013-07-19 14:25:05 -07:00
|
|
|
f.take());
|
2013-07-01 23:24:24 -04:00
|
|
|
main_task.death.on_exit = Some(on_exit);
|
2013-05-28 23:35:22 -07:00
|
|
|
|
|
|
|
let mut threads = ~[];
|
2013-07-19 14:25:05 -07:00
|
|
|
let main_task = Cell::new(main_task);
|
2013-05-28 23:35:22 -07:00
|
|
|
|
2013-07-19 14:25:05 -07:00
|
|
|
let main_thread = {
|
2013-05-28 23:35:22 -07:00
|
|
|
let sched = scheds.pop();
|
2013-06-16 02:03:37 -07:00
|
|
|
let sched_cell = Cell::new(sched);
|
2013-07-19 14:25:05 -07:00
|
|
|
do Thread::start {
|
|
|
|
let sched = sched_cell.take();
|
|
|
|
sched.bootstrap(main_task.take());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
threads.push(main_thread);
|
|
|
|
|
|
|
|
while !scheds.is_empty() {
|
|
|
|
let mut sched = scheds.pop();
|
2013-07-31 13:52:22 -07:00
|
|
|
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool) || {
|
|
|
|
rtdebug!("bootstrapping non-primary scheduler");
|
|
|
|
};
|
2013-07-19 14:25:05 -07:00
|
|
|
let bootstrap_task_cell = Cell::new(bootstrap_task);
|
|
|
|
let sched_cell = Cell::new(sched);
|
2013-05-28 23:35:22 -07:00
|
|
|
let thread = do Thread::start {
|
2013-05-30 13:20:17 -07:00
|
|
|
let sched = sched_cell.take();
|
2013-07-19 14:25:05 -07:00
|
|
|
sched.bootstrap(bootstrap_task_cell.take());
|
2013-05-28 23:35:22 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
threads.push(thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for schedulers
|
2013-08-01 03:16:42 -04:00
|
|
|
foreach thread in threads.consume_iter() {
|
2013-07-27 12:05:15 -07:00
|
|
|
thread.join();
|
|
|
|
}
|
2013-05-28 23:35:22 -07:00
|
|
|
}
|
2013-06-05 22:35:23 -07:00
|
|
|
|
2013-05-28 23:35:22 -07:00
|
|
|
}
|
|
|
|
|
2013-04-23 15:11:28 -07:00
|
|
|
/// Test tasks will abort on failure instead of unwinding
|
|
|
|
pub fn spawntask(f: ~fn()) {
|
2013-07-19 14:25:05 -07:00
|
|
|
Scheduler::run_task(Task::build_child(f));
|
2013-04-20 01:55:10 -07:00
|
|
|
}
|
|
|
|
|
2013-05-06 14:28:16 -07:00
|
|
|
/// Create a new task and run it right now. Aborts on failure
|
|
|
|
pub fn spawntask_later(f: ~fn()) {
|
2013-07-19 14:25:05 -07:00
|
|
|
Scheduler::run_task_later(Task::build_child(f));
|
2013-05-06 14:28:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn spawntask_random(f: ~fn()) {
|
|
|
|
use rand::{Rand, rng};
|
|
|
|
|
2013-06-13 22:43:20 -07:00
|
|
|
let mut rng = rng();
|
|
|
|
let run_now: bool = Rand::rand(&mut rng);
|
2013-05-06 14:28:16 -07:00
|
|
|
|
|
|
|
if run_now {
|
2013-07-19 14:25:05 -07:00
|
|
|
spawntask(f)
|
2013-05-06 14:28:16 -07:00
|
|
|
} else {
|
2013-07-19 14:25:05 -07:00
|
|
|
spawntask_later(f)
|
2013-05-06 14:28:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-19 14:25:05 -07:00
|
|
|
pub fn spawntask_try(f: ~fn()) -> Result<(),()> {
|
2013-06-26 16:41:00 -07:00
|
|
|
|
2013-06-13 23:31:19 -07:00
|
|
|
let (port, chan) = oneshot();
|
2013-06-16 02:03:37 -07:00
|
|
|
let chan = Cell::new(chan);
|
2013-06-13 23:31:19 -07:00
|
|
|
let on_exit: ~fn(bool) = |exit_status| chan.take().send(exit_status);
|
2013-06-26 16:41:00 -07:00
|
|
|
|
2013-07-19 14:25:05 -07:00
|
|
|
let mut new_task = Task::build_root(f);
|
|
|
|
new_task.death.on_exit = Some(on_exit);
|
2013-04-22 17:15:31 -07:00
|
|
|
|
2013-07-19 14:25:05 -07:00
|
|
|
Scheduler::run_task(new_task);
|
2013-06-26 16:41:00 -07:00
|
|
|
|
2013-06-13 23:31:19 -07:00
|
|
|
let exit_status = port.recv();
|
|
|
|
if exit_status { Ok(()) } else { Err(()) }
|
2013-07-19 14:25:05 -07:00
|
|
|
|
2013-04-22 17:15:31 -07:00
|
|
|
}
|
|
|
|
|
2013-07-11 19:42:40 -04:00
|
|
|
/// Spawn a new task in a new scheduler and return a thread handle.
|
2013-05-15 17:20:48 -07:00
|
|
|
pub fn spawntask_thread(f: ~fn()) -> Thread {
|
|
|
|
|
2013-06-26 16:41:00 -07:00
|
|
|
let f = Cell::new(f);
|
|
|
|
|
2013-05-15 17:20:48 -07:00
|
|
|
let thread = do Thread::start {
|
2013-07-19 14:25:05 -07:00
|
|
|
run_in_newsched_task_core(f.take());
|
2013-05-15 17:20:48 -07:00
|
|
|
};
|
2013-07-19 14:25:05 -07:00
|
|
|
|
2013-05-15 17:20:48 -07:00
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
2013-07-11 19:42:40 -04:00
|
|
|
/// Get a ~Task for testing purposes other than actually scheduling it.
|
|
|
|
pub fn with_test_task(blk: ~fn(~Task) -> ~Task) {
|
|
|
|
do run_in_bare_thread {
|
|
|
|
let mut sched = ~new_test_uv_sched();
|
|
|
|
let task = blk(~Task::new_root(&mut sched.stack_pool, ||{}));
|
2013-07-19 14:25:05 -07:00
|
|
|
cleanup_task(task);
|
2013-07-11 19:42:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-19 14:25:05 -07:00
|
|
|
/// Use to cleanup tasks created for testing but not "run".
|
|
|
|
pub fn cleanup_task(mut task: ~Task) {
|
|
|
|
task.destroyed = true;
|
|
|
|
}
|
2013-06-26 16:41:00 -07:00
|
|
|
|
2013-04-20 01:16:06 -07:00
|
|
|
/// Get a port number, starting at 9600, for use in tests
|
|
|
|
pub fn next_test_port() -> u16 {
|
|
|
|
unsafe {
|
2013-07-08 14:41:07 -07:00
|
|
|
return rust_dbg_next_port(base_port() as libc::uintptr_t) as u16;
|
2013-04-20 01:16:06 -07:00
|
|
|
}
|
|
|
|
extern {
|
2013-07-08 14:41:07 -07:00
|
|
|
fn rust_dbg_next_port(base: libc::uintptr_t) -> libc::uintptr_t;
|
2013-04-20 01:16:06 -07:00
|
|
|
}
|
|
|
|
}
|
2013-04-20 02:41:30 -07:00
|
|
|
|
2013-07-02 16:40:57 -07:00
|
|
|
/// Get a unique IPv4 localhost:port pair starting at 9600
|
2013-04-20 02:41:30 -07:00
|
|
|
pub fn next_test_ip4() -> IpAddr {
|
|
|
|
Ipv4(127, 0, 0, 1, next_test_port())
|
|
|
|
}
|
2013-05-15 17:20:48 -07:00
|
|
|
|
2013-07-02 16:40:57 -07:00
|
|
|
/// Get a unique IPv6 localhost:port pair starting at 9600
|
|
|
|
pub fn next_test_ip6() -> IpAddr {
|
|
|
|
Ipv6(0, 0, 0, 0, 0, 0, 0, 1, next_test_port())
|
|
|
|
}
|
|
|
|
|
2013-07-08 14:41:07 -07:00
|
|
|
/*
|
|
|
|
XXX: Welcome to MegaHack City.
|
|
|
|
|
|
|
|
The bots run multiple builds at the same time, and these builds
|
|
|
|
all want to use ports. This function figures out which workspace
|
|
|
|
it is running in and assigns a port range based on it.
|
|
|
|
*/
|
|
|
|
fn base_port() -> uint {
|
|
|
|
use os;
|
|
|
|
use str::StrSlice;
|
|
|
|
use to_str::ToStr;
|
|
|
|
use vec::ImmutableVector;
|
|
|
|
|
|
|
|
let base = 9600u;
|
|
|
|
let range = 1000;
|
|
|
|
|
|
|
|
let bases = [
|
|
|
|
("32-opt", base + range * 1),
|
|
|
|
("32-noopt", base + range * 2),
|
|
|
|
("64-opt", base + range * 3),
|
|
|
|
("64-noopt", base + range * 4),
|
|
|
|
("64-opt-vg", base + range * 5),
|
|
|
|
("all-opt", base + range * 6),
|
|
|
|
("snap3", base + range * 7),
|
|
|
|
("dist", base + range * 8)
|
|
|
|
];
|
|
|
|
|
|
|
|
let path = os::getcwd().to_str();
|
|
|
|
|
|
|
|
let mut final_base = base;
|
|
|
|
|
2013-08-01 03:16:42 -04:00
|
|
|
foreach &(dir, base) in bases.iter() {
|
2013-07-08 14:41:07 -07:00
|
|
|
if path.contains(dir) {
|
|
|
|
final_base = base;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return final_base;
|
|
|
|
}
|
|
|
|
|
2013-06-26 16:41:00 -07:00
|
|
|
/// Get a constant that represents the number of times to repeat
|
|
|
|
/// stress tests. Default 1.
|
2013-05-15 17:20:48 -07:00
|
|
|
pub fn stress_factor() -> uint {
|
|
|
|
use os::getenv;
|
|
|
|
|
|
|
|
match getenv("RUST_RT_STRESS") {
|
|
|
|
Some(val) => uint::from_str(val).get(),
|
|
|
|
None => 1
|
|
|
|
}
|
|
|
|
}
|