Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch Panic Configuration #85

Merged
merged 5 commits into from
Feb 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 32 additions & 19 deletions src/coroutine.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{Event, EventSourceId, RW, coroutine, token_to_ids};
use super::{Event, EventSourceId, RW, coroutine, token_to_ids, sender_retry};
use super::{CoroutineControl};
use super::thread::{TL_CURRENT_COROUTINE};
use super::thread::{HandlerShared};
use super::thread::{HandlerShared, Message};
use super::thread::Handler;
use super::evented::{RcEventSourceTrait, RcEventSource, EventSourceTrait};
use super::thread::RcHandlerShared;
Expand Down Expand Up @@ -168,13 +168,17 @@ pub struct Coroutine {

/// Userdata meant for inheritance
pub inherited_user_data: Option<Arc<Box<Any + Send + Sync>>>,

/// if this coroutine should catch panics
catch_panics: bool,
}

impl Coroutine {
/// Spawn a new Coroutine
pub fn spawn<F>(handler_shared: RcHandlerShared,
inherited_user_data: Option<Arc<Box<Any + Send + Sync>>>,
f: F)
f: F,
catch_panics: bool)
-> RcCoroutine
where F: FnOnce() -> io::Result<()> + Send + 'static
{
Expand Down Expand Up @@ -208,6 +212,7 @@ impl Coroutine {
sync_mailbox: None,
user_data: inherited_user_data.clone(),
inherited_user_data: inherited_user_data,
catch_panics: catch_panics,
};

CoroutineSlabHandle::new(Rc::new(RefCell::new(coroutine)))
Expand All @@ -231,13 +236,14 @@ impl Coroutine {
extern "C" fn init_fn(arg: usize, _: *mut libc::types::common::c95::c_void) -> ! {
let ctx: &Context = {

//never panic inside init_fn, that causes a SIGILL
let res = panic::recover(move || {
let coroutine: &mut Coroutine = unsafe { mem::transmute(arg) };
trace!("Coroutine({}): started", {
coroutine.id.as_usize()
});

coroutine::entry_point(coroutine.self_rc.as_ref().unwrap());
entry_point(coroutine.self_rc.as_ref().unwrap());
let f = coroutine.coroutine_func.take().unwrap();

f.call_box(())
Expand Down Expand Up @@ -269,20 +275,26 @@ impl Coroutine {

}
Err(cause) => {
trace!("Coroutine({}): panicked: {:?}",
id.as_usize(),
cause.downcast::<&str>());
if let State::Finished(ExitStatus::Killed) = coroutine.state {
coroutine.exit_notificators
.iter()
.map(|end| end.send(ExitStatus::Killed))
.count();
if coroutine.catch_panics {
trace!("Coroutine({}): panicked: {:?}",
id.as_usize(),
cause.downcast::<&str>());
if let State::Finished(ExitStatus::Killed) = coroutine.state {
coroutine.exit_notificators
.iter()
.map(|end| end.send(ExitStatus::Killed))
.count();
} else {
coroutine.state = State::Finished(ExitStatus::Panic);
coroutine.exit_notificators
.iter()
.map(|end| end.send(ExitStatus::Panic))
.count();
}
} else {
coroutine.state = State::Finished(ExitStatus::Panic);
coroutine.exit_notificators
.iter()
.map(|end| end.send(ExitStatus::Panic))
.count();
//send fail here instead with the internal reason, so the user may get a nice backtrace
let handler = coroutine.handler_shared.as_ref().unwrap().borrow();
sender_retry(&handler.get_sender_to_own_thread(), Message::PropagatePanic(cause));
}
}
}
Expand Down Expand Up @@ -314,7 +326,8 @@ impl Coroutine {
let child = Coroutine::spawn(
self.handler_shared.as_ref().unwrap().clone(),
self.inherited_user_data.clone(),
f);
f,
self.catch_panics);
self.children_to_start.push(child.clone());
child
}
Expand Down Expand Up @@ -348,7 +361,7 @@ impl Coroutine {
pub fn finish(&mut self) {
self.state = coroutine::State::Finished(coroutine::ExitStatus::Killed)
}

pub fn unblock_after_yield(&mut self) {
self.state = coroutine::State::Ready;
}
Expand Down
28 changes: 21 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

#![feature(recover)]
#![feature(std_panic)]
#![feature(panic_propagate)]
#![feature(fnbox)]
#![feature(cell_extras)]
#![feature(as_unsafe_cell)]
Expand Down Expand Up @@ -239,7 +240,6 @@ fn token_from_ids(co_id: coroutine::Id, io_id: EventSourceId) -> Token {
Token((co_id.as_usize() << EVENT_SOURCE_TOKEN_SHIFT) | io_id.as_usize())
}


/// Id of an event source used to enumerate them
///
/// It's unique within coroutine of an event source, but not globally.
Expand Down Expand Up @@ -594,6 +594,7 @@ impl Mioco {

let scheduler = self.config.scheduler.clone();
let stack_size = self.config.stack_size;
let catch_panics = self.config.catch_panics;
let event_loop = event_loops.pop_front().unwrap();
let senders = senders.clone();
let thread_shared = thread_shared.clone();
Expand All @@ -604,10 +605,12 @@ impl Mioco {
Mioco::thread_loop::<F>(None,
sched,
event_loop,
i,
senders,
thread_shared,
stack_size,
None);
None,
catch_panics);
});

match join {
Expand All @@ -621,10 +624,12 @@ impl Mioco {
Mioco::thread_loop(Some(f),
sched,
first_event_loop,
0,
senders,
thread_shared,
self.config.stack_size,
user_data);
user_data,
self.config.catch_panics);

for join in self.join_handles.drain(..) {
let _ = join.join(); // TODO: Do something with it
Expand All @@ -634,17 +639,19 @@ impl Mioco {
fn thread_loop<F>(f: Option<F>,
mut scheduler: Box<SchedulerThread + 'static>,
mut event_loop: EventLoop<thread::Handler>,
thread_id: usize,
senders: Vec<thread::MioSender>,
thread_shared: thread::ArcHandlerThreadShared,
stack_size: usize,
userdata: Option<Arc<Box<Any + Send + Sync>>>)
userdata: Option<Arc<Box<Any + Send + Sync>>>,
catch_panics: bool)
where F: FnOnce() -> io::Result<()> + Send + 'static,
F: Send
{
let handler_shared = thread::HandlerShared::new(senders, thread_shared, stack_size);
let handler_shared = thread::HandlerShared::new(senders, thread_shared, stack_size, thread_id);
let shared = Rc::new(RefCell::new(handler_shared));
if let Some(f) = f {
let coroutine_rc = Coroutine::spawn(shared.clone(), userdata, f);
let coroutine_rc = Coroutine::spawn(shared.clone(), userdata, f, catch_panics);
let coroutine_ctrl = CoroutineControl::new(coroutine_rc);
scheduler.spawned(&mut event_loop, coroutine_ctrl);
// Mark started only after first coroutine is spawned so that
Expand All @@ -670,6 +677,7 @@ pub struct Config {
event_loop_config: EventLoopConfig,
stack_size: usize,
user_data: Option<Arc<Box<Any + Send + Sync>>>,
catch_panics: bool,
}

impl Config {
Expand All @@ -685,6 +693,7 @@ impl Config {
event_loop_config: Default::default(),
stack_size: 2 * 1024 * 1024,
user_data: None,
catch_panics: true,
};
config
}
Expand Down Expand Up @@ -738,6 +747,12 @@ impl Config {
pub fn even_loop(&mut self) -> &mut EventLoopConfig {
&mut self.event_loop_config
}

/// Set if this Instance will be catching panics, that occure within the coroutines
pub fn set_catch_panics(&mut self, catch_panics: bool) -> &mut Self {
self.catch_panics = catch_panics;
self
}
}

// TODO: Technically this leaks unsafe, but only within
Expand Down Expand Up @@ -818,7 +833,6 @@ pub fn spawn_ext<F>(f: F) -> CoroutineHandle
where F: FnOnce() -> io::Result<()> + Send + 'static
{
let coroutine = tl_coroutine_current();

CoroutineHandle { coroutine: coroutine.spawn_child(f) }
}

Expand Down
16 changes: 16 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ fn contain_panics_in_subcoroutines() {
}
}

#[test]
#[should_panic]
#[cfg(debug_assertions)] //optimizations seem to let this test fail. lets disable that for now.
fn propagate_uncatched_panic() {
use ::{Mioco, Config};

Mioco::new_configured({
let mut config = Config::new();
config.set_catch_panics(false);
config.set_thread_num(1);
config
}).start(|| {
panic!()
});
}

#[test]
fn long_chain() {
for &threads in THREADS_N.iter() {
Expand Down
16 changes: 14 additions & 2 deletions src/thread.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std;
use std::any::Any;
use std::cell::{RefCell};
use std::rc::Rc;
use std::sync::Arc;
use std::panic;
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};

Expand Down Expand Up @@ -68,12 +70,15 @@ pub struct HandlerShared {

/// Coroutines that were made ready
ready: Vec<CoroutineControl>,

thread_id: usize,
}

impl HandlerShared {
pub fn new(senders: Vec<MioSender>,
thread_shared: ArcHandlerThreadShared,
stack_size: usize)
stack_size: usize,
thread_id: usize)
-> Self {
HandlerShared {
coroutines: slab::Slab::new(512),
Expand All @@ -83,6 +88,7 @@ impl HandlerShared {
stack_size: stack_size,
spawned: Vec::new(),
ready: Vec::new(),
thread_id: thread_id,
}
}

Expand All @@ -94,6 +100,10 @@ impl HandlerShared {
self.ready.push(coroutine_ctrl);
}

pub fn get_sender_to_own_thread(&self) -> MioSender {
self.senders[self.thread_id].clone()
}

pub fn get_sender_to_thread(&self, thread_id : usize) -> MioSender {
self.senders[thread_id].clone()
}
Expand Down Expand Up @@ -205,6 +215,8 @@ pub enum Message {
MailboxMsg(Token),
/// Coroutine migration
Migration(CoroutineControl),
/// Coroutine Panicked
PropagatePanic(Box<Any + Send + 'static>),
}

unsafe impl Send for Message {}
Expand Down Expand Up @@ -256,11 +268,11 @@ impl mio_orig::Handler for Handler {
self.scheduler.ready(event_loop, coroutine);
self.deliver_to_scheduler(event_loop);
}
Message::PropagatePanic(cause) => panic::propagate(cause),
}
}

fn timeout(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Timeout) {
self.ready(event_loop, msg, EventSet::readable());
}
}