core::rt: Tasks to not require an unwinder
A task without an unwinder will abort the process on failure. I'm using this in the runtime tests to guarantee that a call to `assert!` actually triggers some kind of failure (an abort) instead of silently doing nothing. This is essentially in lieu of a working linked failure implementation.
This commit is contained in:
parent
f4af40a1db
commit
2a819ae465
|
@ -114,6 +114,9 @@ pub mod linkhack {
|
|||
}
|
||||
}
|
||||
|
||||
// Internal macros
|
||||
mod macros;
|
||||
|
||||
/* The Prelude. */
|
||||
|
||||
pub mod prelude;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
#[macro_escape];
|
||||
|
||||
// Some basic logging
|
||||
macro_rules! rtdebug (
|
||||
($( $arg:expr),+) => ( {
|
||||
dumb_println(fmt!( $($arg),+ ));
|
||||
|
||||
fn dumb_println(s: &str) {
|
||||
use io::WriterUtil;
|
||||
let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
|
||||
dbg.write_str(s);
|
||||
dbg.write_str("\n");
|
||||
}
|
||||
|
||||
} )
|
||||
)
|
||||
|
||||
// An alternate version with no output, for turning off logging
|
||||
macro_rules! rtdebug_ (
|
||||
($( $arg:expr),+) => ( $(let _ = $arg)*; )
|
||||
)
|
||||
|
||||
macro_rules! abort(
|
||||
($( $msg:expr),+) => ( {
|
||||
rtdebug!($($msg),+);
|
||||
|
||||
unsafe { ::libc::abort(); }
|
||||
} )
|
||||
)
|
|
@ -29,7 +29,7 @@ pub struct LocalServices {
|
|||
gc: GarbageCollector,
|
||||
storage: LocalStorage,
|
||||
logger: Logger,
|
||||
unwinder: Unwinder,
|
||||
unwinder: Option<Unwinder>,
|
||||
destroyed: bool
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,18 @@ impl LocalServices {
|
|||
gc: GarbageCollector,
|
||||
storage: LocalStorage(ptr::null(), None),
|
||||
logger: Logger,
|
||||
unwinder: Unwinder { unwinding: false },
|
||||
unwinder: Some(Unwinder { unwinding: false }),
|
||||
destroyed: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn without_unwinding() -> LocalServices {
|
||||
LocalServices {
|
||||
heap: LocalHeap::new(),
|
||||
gc: GarbageCollector,
|
||||
storage: LocalStorage(ptr::null(), None),
|
||||
logger: Logger,
|
||||
unwinder: None,
|
||||
destroyed: false
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +71,16 @@ impl LocalServices {
|
|||
assert!(ptr::ref_eq(sched, self));
|
||||
}
|
||||
|
||||
self.unwinder.try(f);
|
||||
match self.unwinder {
|
||||
Some(ref mut unwinder) => {
|
||||
// If there's an unwinder then set up the catch block
|
||||
unwinder.try(f);
|
||||
}
|
||||
None => {
|
||||
// Otherwise, just run the body
|
||||
f()
|
||||
}
|
||||
}
|
||||
self.destroy();
|
||||
}
|
||||
|
||||
|
@ -189,9 +209,9 @@ mod test {
|
|||
#[test]
|
||||
fn unwind() {
|
||||
do run_in_newsched_task() {
|
||||
let result = spawn_try(||());
|
||||
let result = spawntask_try(||());
|
||||
assert!(result.is_ok());
|
||||
let result = spawn_try(|| fail!());
|
||||
let result = spawntask_try(|| fail!());
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,26 +12,6 @@
|
|||
|
||||
use libc::c_char;
|
||||
|
||||
// Some basic logging
|
||||
macro_rules! rtdebug_ (
|
||||
($( $arg:expr),+) => ( {
|
||||
dumb_println(fmt!( $($arg),+ ));
|
||||
|
||||
fn dumb_println(s: &str) {
|
||||
use io::WriterUtil;
|
||||
let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
|
||||
dbg.write_str(s);
|
||||
dbg.write_str("\n");
|
||||
}
|
||||
|
||||
} )
|
||||
)
|
||||
|
||||
// An alternate version with no output, for turning off logging
|
||||
macro_rules! rtdebug (
|
||||
($( $arg:expr),+) => ( $(let _ = $arg)*; )
|
||||
)
|
||||
|
||||
#[path = "sched/mod.rs"]
|
||||
mod sched;
|
||||
mod rtio;
|
||||
|
|
|
@ -149,7 +149,7 @@ pub impl Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
// Control never reaches here
|
||||
abort!("control reached end of task");
|
||||
}
|
||||
|
||||
fn schedule_new_task(~self, task: ~Task) {
|
||||
|
@ -333,6 +333,12 @@ pub struct Task {
|
|||
|
||||
pub impl Task {
|
||||
fn new(stack_pool: &mut StackPool, start: ~fn()) -> Task {
|
||||
Task::with_local(stack_pool, LocalServices::new(), start)
|
||||
}
|
||||
|
||||
fn with_local(stack_pool: &mut StackPool,
|
||||
local_services: LocalServices,
|
||||
start: ~fn()) -> Task {
|
||||
let start = Task::build_start_wrapper(start);
|
||||
let mut stack = stack_pool.take_segment(TASK_MIN_STACK_SIZE);
|
||||
// NB: Context holds a pointer to that ~fn
|
||||
|
@ -340,7 +346,7 @@ pub impl Task {
|
|||
return Task {
|
||||
current_stack_segment: stack,
|
||||
saved_context: initial_context,
|
||||
local_services: LocalServices::new()
|
||||
local_services: local_services
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,38 +8,56 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use cell::Cell;
|
||||
use result::{Result, Ok, Err};
|
||||
use super::io::net::ip::{IpAddr, Ipv4};
|
||||
use rt::local_services::LocalServices;
|
||||
|
||||
/// Creates a new scheduler in a new thread and runs a task in it,
|
||||
/// then waits for the scheduler to exit.
|
||||
/// then waits for the scheduler to exit. Failure of the task
|
||||
/// will abort the process.
|
||||
pub fn run_in_newsched_task(f: ~fn()) {
|
||||
use cell::Cell;
|
||||
use unstable::run_in_bare_thread;
|
||||
use super::sched::Task;
|
||||
use super::uvio::UvEventLoop;
|
||||
|
||||
let f = Cell(Cell(f));
|
||||
let f = Cell(f);
|
||||
|
||||
do run_in_bare_thread {
|
||||
let mut sched = ~UvEventLoop::new_scheduler();
|
||||
let f = f.take();
|
||||
let task = ~do Task::new(&mut sched.stack_pool) {
|
||||
(f.take())();
|
||||
};
|
||||
let task = ~Task::with_local(&mut sched.stack_pool,
|
||||
LocalServices::without_unwinding(),
|
||||
f.take());
|
||||
sched.task_queue.push_back(task);
|
||||
sched.run();
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new task and run it right now
|
||||
pub fn spawn_immediately(f: ~fn()) {
|
||||
use cell::Cell;
|
||||
/// Test tasks will abort on failure instead of unwinding
|
||||
pub fn spawntask(f: ~fn()) {
|
||||
use super::*;
|
||||
use super::sched::*;
|
||||
|
||||
let mut sched = local_sched::take();
|
||||
let task = ~Task::new(&mut sched.stack_pool, f);
|
||||
let task = ~Task::with_local(&mut sched.stack_pool,
|
||||
LocalServices::without_unwinding(),
|
||||
f);
|
||||
do sched.switch_running_tasks_and_then(task) |task| {
|
||||
let task = Cell(task);
|
||||
let sched = local_sched::take();
|
||||
sched.schedule_new_task(task.take());
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new task and run it right now. Aborts on failure
|
||||
pub fn spawntask_immediately(f: ~fn()) {
|
||||
use super::*;
|
||||
use super::sched::*;
|
||||
|
||||
let mut sched = local_sched::take();
|
||||
let task = ~Task::with_local(&mut sched.stack_pool,
|
||||
LocalServices::without_unwinding(),
|
||||
f);
|
||||
do sched.switch_running_tasks_and_then(task) |task| {
|
||||
let task = Cell(task);
|
||||
do local_sched::borrow |sched| {
|
||||
|
@ -49,7 +67,7 @@ pub fn spawn_immediately(f: ~fn()) {
|
|||
}
|
||||
|
||||
/// Spawn a task and wait for it to finish, returning whether it completed successfully or failed
|
||||
pub fn spawn_try(f: ~fn()) -> Result<(), ()> {
|
||||
pub fn spawntask_try(f: ~fn()) -> Result<(), ()> {
|
||||
use cell::Cell;
|
||||
use super::sched::*;
|
||||
use task;
|
||||
|
|
|
@ -350,7 +350,7 @@ fn test_simple_tcp_server_and_client() {
|
|||
let addr = next_test_ip4();
|
||||
|
||||
// Start the server first so it's listening when we connect
|
||||
do spawn_immediately {
|
||||
do spawntask_immediately {
|
||||
unsafe {
|
||||
let io = local_sched::unsafe_borrow_io();
|
||||
let mut listener = io.bind(addr).unwrap();
|
||||
|
@ -367,7 +367,7 @@ fn test_simple_tcp_server_and_client() {
|
|||
}
|
||||
}
|
||||
|
||||
do spawn_immediately {
|
||||
do spawntask_immediately {
|
||||
unsafe {
|
||||
let io = local_sched::unsafe_borrow_io();
|
||||
let mut stream = io.connect(addr).unwrap();
|
||||
|
@ -383,7 +383,7 @@ fn test_read_and_block() {
|
|||
do run_in_newsched_task {
|
||||
let addr = next_test_ip4();
|
||||
|
||||
do spawn_immediately {
|
||||
do spawntask_immediately {
|
||||
let io = unsafe { local_sched::unsafe_borrow_io() };
|
||||
let mut listener = io.bind(addr).unwrap();
|
||||
let mut stream = listener.listen().unwrap();
|
||||
|
@ -421,7 +421,7 @@ fn test_read_and_block() {
|
|||
listener.close();
|
||||
}
|
||||
|
||||
do spawn_immediately {
|
||||
do spawntask_immediately {
|
||||
let io = unsafe { local_sched::unsafe_borrow_io() };
|
||||
let mut stream = io.connect(addr).unwrap();
|
||||
stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
|
@ -440,7 +440,7 @@ fn test_read_read_read() {
|
|||
let addr = next_test_ip4();
|
||||
static MAX: uint = 500000;
|
||||
|
||||
do spawn_immediately {
|
||||
do spawntask_immediately {
|
||||
unsafe {
|
||||
let io = local_sched::unsafe_borrow_io();
|
||||
let mut listener = io.bind(addr).unwrap();
|
||||
|
@ -456,7 +456,7 @@ fn test_read_read_read() {
|
|||
}
|
||||
}
|
||||
|
||||
do spawn_immediately {
|
||||
do spawntask_immediately {
|
||||
let io = unsafe { local_sched::unsafe_borrow_io() };
|
||||
let mut stream = io.connect(addr).unwrap();
|
||||
let mut buf = [0, .. 2048];
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
//! Misc low level stuff
|
||||
|
||||
use option::{Some, None};
|
||||
use cast;
|
||||
use cmp::{Eq, Ord};
|
||||
use gc;
|
||||
|
@ -154,7 +155,10 @@ pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! {
|
|||
gc::cleanup_stack_for_failure();
|
||||
unsafe {
|
||||
let local_services = unsafe_borrow_local_services();
|
||||
local_services.unwinder.begin_unwind();
|
||||
match local_services.unwinder {
|
||||
Some(ref mut unwinder) => unwinder.begin_unwind(),
|
||||
None => abort!("failure without unwinder. aborting process")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -570,7 +570,16 @@ pub fn failing() -> bool {
|
|||
_ => {
|
||||
let mut unwinding = false;
|
||||
do borrow_local_services |local| {
|
||||
unwinding = local.unwinder.unwinding;
|
||||
unwinding = match local.unwinder {
|
||||
Some(unwinder) => {
|
||||
unwinder.unwinding
|
||||
}
|
||||
None => {
|
||||
// Because there is no unwinder we can't be unwinding.
|
||||
// (The process will abort on failure)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
return unwinding;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue