2013-04-20 02:33:49 -05: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-05-15 19:20:48 -05:00
|
|
|
use uint;
|
2013-05-30 15:20:17 -05:00
|
|
|
use option::{Some, None};
|
2013-04-23 17:11:28 -05:00
|
|
|
use cell::Cell;
|
2013-05-29 01:35:22 -05:00
|
|
|
use clone::Clone;
|
|
|
|
use container::Container;
|
2013-05-29 17:55:23 -05:00
|
|
|
use old_iter::MutableIter;
|
2013-05-29 01:35:22 -05:00
|
|
|
use vec::OwnedVector;
|
2013-04-22 19:15:31 -05:00
|
|
|
use result::{Result, Ok, Err};
|
2013-05-29 01:35:22 -05:00
|
|
|
use unstable::run_in_bare_thread;
|
2013-04-20 04:41:30 -05:00
|
|
|
use super::io::net::ip::{IpAddr, Ipv4};
|
2013-05-19 03:04:01 -05:00
|
|
|
use rt::task::Task;
|
2013-05-15 19:20:48 -05:00
|
|
|
use rt::thread::Thread;
|
2013-05-19 17:45:39 -05:00
|
|
|
use rt::local::Local;
|
2013-05-29 01:35:22 -05:00
|
|
|
use rt::sched::{Scheduler, Coroutine};
|
|
|
|
use rt::sleeper_list::SleeperList;
|
|
|
|
use rt::work_queue::WorkQueue;
|
2013-05-23 00:18:29 -05:00
|
|
|
|
|
|
|
pub fn new_test_uv_sched() -> Scheduler {
|
|
|
|
use rt::uv::uvio::UvEventLoop;
|
|
|
|
use rt::work_queue::WorkQueue;
|
2013-05-28 21:53:55 -05:00
|
|
|
use rt::sleeper_list::SleeperList;
|
2013-05-23 00:18:29 -05:00
|
|
|
|
2013-05-29 17:55:23 -05:00
|
|
|
let mut sched = Scheduler::new(~UvEventLoop::new(), WorkQueue::new(), SleeperList::new());
|
|
|
|
// Don't wait for the Shutdown message
|
|
|
|
sched.no_sleep = true;
|
|
|
|
return sched;
|
2013-05-23 00:18:29 -05:00
|
|
|
}
|
2013-04-20 04:41:30 -05:00
|
|
|
|
2013-04-20 17:55:07 -05:00
|
|
|
/// Creates a new scheduler in a new thread and runs a task in it,
|
2013-04-23 17:11:28 -05:00
|
|
|
/// then waits for the scheduler to exit. Failure of the task
|
|
|
|
/// will abort the process.
|
2013-04-20 02:33:49 -05:00
|
|
|
pub fn run_in_newsched_task(f: ~fn()) {
|
2013-05-15 19:20:48 -05:00
|
|
|
use super::sched::*;
|
2013-04-20 02:33:49 -05:00
|
|
|
use unstable::run_in_bare_thread;
|
|
|
|
|
2013-04-23 17:11:28 -05:00
|
|
|
let f = Cell(f);
|
2013-04-20 02:33:49 -05:00
|
|
|
|
|
|
|
do run_in_bare_thread {
|
2013-05-23 00:18:29 -05:00
|
|
|
let mut sched = ~new_test_uv_sched();
|
2013-05-19 03:04:01 -05:00
|
|
|
let task = ~Coroutine::with_task(&mut sched.stack_pool,
|
2013-05-19 16:05:20 -05:00
|
|
|
~Task::without_unwinding(),
|
2013-05-19 03:04:01 -05:00
|
|
|
f.take());
|
2013-05-11 02:42:16 -05:00
|
|
|
sched.enqueue_task(task);
|
2013-04-20 02:33:49 -05:00
|
|
|
sched.run();
|
|
|
|
}
|
|
|
|
}
|
2013-04-20 03:16:06 -05:00
|
|
|
|
2013-05-29 01:35:22 -05: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()) {
|
|
|
|
use rt::uv::uvio::UvEventLoop;
|
2013-05-29 17:55:23 -05:00
|
|
|
use rt::sched::Shutdown;
|
2013-05-29 01:35:22 -05:00
|
|
|
|
|
|
|
let f_cell = Cell(f);
|
|
|
|
|
|
|
|
do run_in_bare_thread {
|
2013-05-30 02:18:49 -05:00
|
|
|
static N: uint = 4;
|
2013-05-29 01:35:22 -05:00
|
|
|
|
|
|
|
let sleepers = SleeperList::new();
|
|
|
|
let work_queue = WorkQueue::new();
|
|
|
|
|
|
|
|
let mut handles = ~[];
|
|
|
|
let mut scheds = ~[];
|
|
|
|
|
2013-05-30 15:20:17 -05:00
|
|
|
for uint::range(0, N) |_| {
|
2013-05-29 01:35:22 -05:00
|
|
|
let loop_ = ~UvEventLoop::new();
|
|
|
|
let mut sched = ~Scheduler::new(loop_, work_queue.clone(), sleepers.clone());
|
|
|
|
let handle = sched.make_handle();
|
|
|
|
handles.push(handle);
|
|
|
|
scheds.push(sched);
|
|
|
|
}
|
|
|
|
|
|
|
|
let f_cell = Cell(f_cell.take());
|
2013-05-29 17:55:23 -05:00
|
|
|
let handles = Cell(handles);
|
2013-05-29 01:35:22 -05:00
|
|
|
let main_task = ~do Coroutine::new(&mut scheds[0].stack_pool) {
|
|
|
|
f_cell.take()();
|
2013-05-29 17:55:23 -05:00
|
|
|
|
|
|
|
let mut handles = handles.take();
|
|
|
|
// Tell schedulers to exit
|
|
|
|
for handles.each_mut |handle| {
|
|
|
|
handle.send(Shutdown);
|
|
|
|
}
|
2013-05-29 01:35:22 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
scheds[0].enqueue_task(main_task);
|
|
|
|
|
|
|
|
let mut threads = ~[];
|
|
|
|
|
|
|
|
while !scheds.is_empty() {
|
|
|
|
let sched = scheds.pop();
|
|
|
|
let sched_cell = Cell(sched);
|
|
|
|
let thread = do Thread::start {
|
2013-05-30 15:20:17 -05:00
|
|
|
let sched = sched_cell.take();
|
2013-05-29 01:35:22 -05:00
|
|
|
sched.run();
|
|
|
|
};
|
|
|
|
|
|
|
|
threads.push(thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for schedulers
|
|
|
|
let _threads = threads;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-23 17:11:28 -05:00
|
|
|
/// Test tasks will abort on failure instead of unwinding
|
|
|
|
pub fn spawntask(f: ~fn()) {
|
|
|
|
use super::sched::*;
|
|
|
|
|
2013-05-19 17:45:39 -05:00
|
|
|
let mut sched = Local::take::<Scheduler>();
|
2013-05-19 03:04:01 -05:00
|
|
|
let task = ~Coroutine::with_task(&mut sched.stack_pool,
|
2013-05-19 16:05:20 -05:00
|
|
|
~Task::without_unwinding(),
|
2013-05-19 03:04:01 -05:00
|
|
|
f);
|
2013-05-29 19:52:00 -05:00
|
|
|
sched.schedule_new_task(task);
|
2013-04-23 17:11:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a new task and run it right now. Aborts on failure
|
|
|
|
pub fn spawntask_immediately(f: ~fn()) {
|
2013-04-20 03:55:10 -05:00
|
|
|
use super::sched::*;
|
|
|
|
|
2013-05-19 17:45:39 -05:00
|
|
|
let mut sched = Local::take::<Scheduler>();
|
2013-05-19 03:04:01 -05:00
|
|
|
let task = ~Coroutine::with_task(&mut sched.stack_pool,
|
2013-05-19 16:05:20 -05:00
|
|
|
~Task::without_unwinding(),
|
2013-05-19 03:04:01 -05:00
|
|
|
f);
|
2013-05-29 19:52:00 -05:00
|
|
|
do sched.switch_running_tasks_and_then(task) |sched, task| {
|
|
|
|
sched.enqueue_task(task);
|
2013-04-20 03:55:10 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-06 16:28:16 -05:00
|
|
|
/// Create a new task and run it right now. Aborts on failure
|
|
|
|
pub fn spawntask_later(f: ~fn()) {
|
|
|
|
use super::sched::*;
|
|
|
|
|
2013-05-19 17:45:39 -05:00
|
|
|
let mut sched = Local::take::<Scheduler>();
|
2013-05-19 03:04:01 -05:00
|
|
|
let task = ~Coroutine::with_task(&mut sched.stack_pool,
|
2013-05-19 16:05:20 -05:00
|
|
|
~Task::without_unwinding(),
|
2013-05-19 03:04:01 -05:00
|
|
|
f);
|
2013-05-06 16:28:16 -05:00
|
|
|
|
2013-05-11 02:42:16 -05:00
|
|
|
sched.enqueue_task(task);
|
2013-05-19 17:45:39 -05:00
|
|
|
Local::put(sched);
|
2013-05-06 16:28:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Spawn a task and either run it immediately or run it later
|
|
|
|
pub fn spawntask_random(f: ~fn()) {
|
|
|
|
use super::sched::*;
|
|
|
|
use rand::{Rand, rng};
|
|
|
|
|
|
|
|
let mut rng = rng();
|
|
|
|
let run_now: bool = Rand::rand(&mut rng);
|
|
|
|
|
2013-05-19 17:45:39 -05:00
|
|
|
let mut sched = Local::take::<Scheduler>();
|
2013-05-19 03:04:01 -05:00
|
|
|
let task = ~Coroutine::with_task(&mut sched.stack_pool,
|
2013-05-19 16:05:20 -05:00
|
|
|
~Task::without_unwinding(),
|
2013-05-19 03:04:01 -05:00
|
|
|
f);
|
2013-05-06 16:28:16 -05:00
|
|
|
|
|
|
|
if run_now {
|
2013-05-29 19:52:00 -05:00
|
|
|
do sched.switch_running_tasks_and_then(task) |sched, task| {
|
|
|
|
sched.enqueue_task(task);
|
2013-05-06 16:28:16 -05:00
|
|
|
}
|
|
|
|
} else {
|
2013-05-11 02:42:16 -05:00
|
|
|
sched.enqueue_task(task);
|
2013-05-19 17:45:39 -05:00
|
|
|
Local::put(sched);
|
2013-05-06 16:28:16 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-22 19:15:31 -05:00
|
|
|
/// Spawn a task and wait for it to finish, returning whether it completed successfully or failed
|
2013-04-23 17:11:28 -05:00
|
|
|
pub fn spawntask_try(f: ~fn()) -> Result<(), ()> {
|
2013-04-22 19:15:31 -05:00
|
|
|
use cell::Cell;
|
|
|
|
use super::sched::*;
|
|
|
|
use task;
|
|
|
|
use unstable::finally::Finally;
|
|
|
|
|
|
|
|
// Our status variables will be filled in from the scheduler context
|
|
|
|
let mut failed = false;
|
|
|
|
let failed_ptr: *mut bool = &mut failed;
|
|
|
|
|
|
|
|
// Switch to the scheduler
|
|
|
|
let f = Cell(Cell(f));
|
2013-05-19 17:45:39 -05:00
|
|
|
let sched = Local::take::<Scheduler>();
|
2013-05-29 19:52:00 -05:00
|
|
|
do sched.deschedule_running_task_and_then() |sched, old_task| {
|
2013-04-22 19:15:31 -05:00
|
|
|
let old_task = Cell(old_task);
|
|
|
|
let f = f.take();
|
2013-05-12 17:26:19 -05:00
|
|
|
let new_task = ~do Coroutine::new(&mut sched.stack_pool) {
|
2013-04-22 19:15:31 -05:00
|
|
|
do (|| {
|
|
|
|
(f.take())()
|
|
|
|
}).finally {
|
|
|
|
// Check for failure then resume the parent task
|
|
|
|
unsafe { *failed_ptr = task::failing(); }
|
2013-05-19 17:45:39 -05:00
|
|
|
let sched = Local::take::<Scheduler>();
|
2013-05-29 19:52:00 -05:00
|
|
|
do sched.switch_running_tasks_and_then(old_task.take()) |sched, new_task| {
|
|
|
|
sched.enqueue_task(new_task);
|
2013-04-22 19:15:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-05-29 19:52:00 -05:00
|
|
|
sched.enqueue_task(new_task);
|
2013-04-22 19:15:31 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if !failed { Ok(()) } else { Err(()) }
|
|
|
|
}
|
|
|
|
|
2013-05-15 19:20:48 -05:00
|
|
|
// Spawn a new task in a new scheduler and return a thread handle.
|
|
|
|
pub fn spawntask_thread(f: ~fn()) -> Thread {
|
|
|
|
use rt::sched::*;
|
|
|
|
|
|
|
|
let f = Cell(f);
|
|
|
|
let thread = do Thread::start {
|
2013-05-23 00:18:29 -05:00
|
|
|
let mut sched = ~new_test_uv_sched();
|
2013-05-19 03:04:01 -05:00
|
|
|
let task = ~Coroutine::with_task(&mut sched.stack_pool,
|
2013-05-19 16:05:20 -05:00
|
|
|
~Task::without_unwinding(),
|
2013-05-19 03:04:01 -05:00
|
|
|
f.take());
|
2013-05-15 19:20:48 -05:00
|
|
|
sched.enqueue_task(task);
|
|
|
|
sched.run();
|
|
|
|
};
|
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
2013-04-20 03:16:06 -05:00
|
|
|
/// Get a port number, starting at 9600, for use in tests
|
|
|
|
pub fn next_test_port() -> u16 {
|
|
|
|
unsafe {
|
|
|
|
return rust_dbg_next_port() as u16;
|
|
|
|
}
|
|
|
|
extern {
|
|
|
|
fn rust_dbg_next_port() -> ::libc::uintptr_t;
|
|
|
|
}
|
|
|
|
}
|
2013-04-20 04:41:30 -05:00
|
|
|
|
|
|
|
/// Get a unique localhost:port pair starting at 9600
|
|
|
|
pub fn next_test_ip4() -> IpAddr {
|
|
|
|
Ipv4(127, 0, 0, 1, next_test_port())
|
|
|
|
}
|
2013-05-15 19:20:48 -05:00
|
|
|
|
|
|
|
/// Get a constant that represents the number of times to repeat stress tests. Default 1.
|
|
|
|
pub fn stress_factor() -> uint {
|
|
|
|
use os::getenv;
|
|
|
|
|
|
|
|
match getenv("RUST_RT_STRESS") {
|
|
|
|
Some(val) => uint::from_str(val).get(),
|
|
|
|
None => 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|