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:
Brian Anderson 2013-04-23 15:11:28 -07:00
parent f4af40a1db
commit 2a819ae465
9 changed files with 126 additions and 47 deletions

View File

@ -114,6 +114,9 @@ pub mod linkhack {
}
}
// Internal macros
mod macros;
/* The Prelude. */
pub mod prelude;

39
src/libcore/macros.rs Normal file
View File

@ -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(); }
} )
)

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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
};
}

View File

@ -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;

View File

@ -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];

View File

@ -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")
}
}
}
}

View File

@ -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;
}