core: add previous-handler save and restore for proper nesting.

This commit is contained in:
Graydon Hoare 2012-10-18 12:27:09 -07:00
parent 754704ea94
commit acf2d208d6
1 changed files with 78 additions and 16 deletions

View File

@ -6,9 +6,8 @@ struct Condition {
} }
struct Handler { struct Handler {
// Handler should link to previous handler and handle: RustClosure,
// reinstall it when popped. prev: Option<@Handler>
handle: RustClosure
} }
@ -21,7 +20,17 @@ struct PopHandler {
cond: &Condition, cond: &Condition,
drop { drop {
unsafe { unsafe {
task::local_data::local_data_pop(self.cond.key); debug!("PopHandler: popping handler from TLS");
match task::local_data::local_data_pop(self.cond.key) {
None => (),
Some(h) => {
match h.prev {
None => (),
Some(p) =>
task::local_data::local_data_set(self.cond.key, p)
}
}
}
} }
} }
} }
@ -31,13 +40,16 @@ struct HandleBlock {
handler: @Handler, handler: @Handler,
drop { drop {
unsafe { unsafe {
debug!("HandleBlock: pushing handler to TLS");
task::local_data::local_data_set(self.pb.cond.key, task::local_data::local_data_set(self.pb.cond.key,
self.handler); self.handler);
let _pop = PopHandler { cond: self.pb.cond }; let _pop = PopHandler { cond: self.pb.cond };
// transmutation to avoid copying non-copyable, should // transmutation to avoid copying non-copyable, should
// be fixable by tracking closure pointees in regionck. // be fixable by tracking closure pointees in regionck.
let f : &fn() = ::cast::transmute(self.pb.inner); let f : &fn() = ::cast::transmute(self.pb.inner);
debug!("HandleBlock: invoking protected code");
f(); f();
debug!("HandleBlock: returned from protected code");
} }
} }
} }
@ -45,9 +57,11 @@ struct HandleBlock {
impl ProtectBlock { impl ProtectBlock {
fn handle<T, U: Copy>(&self, h: &self/fn(&T) ->U) -> HandleBlock/&self { fn handle<T, U: Copy>(&self, h: &self/fn(&T) ->U) -> HandleBlock/&self {
unsafe { unsafe {
debug!("ProtectBlock.handle: setting up handler block");
let p : *RustClosure = ::cast::transmute(&h); let p : *RustClosure = ::cast::transmute(&h);
let prev = task::local_data::local_data_get(self.cond.key);
HandleBlock { pb: self, HandleBlock { pb: self,
handler: @Handler{handle: *p} } handler: @Handler{handle: *p, prev: prev} }
} }
} }
} }
@ -59,17 +73,23 @@ impl Condition {
unsafe { unsafe {
// transmutation to avoid copying non-copyable, should // transmutation to avoid copying non-copyable, should
// be fixable by tracking closure pointees in regionck. // be fixable by tracking closure pointees in regionck.
debug!("Condition.protect: setting up protected block");
let p : *RustClosure = ::cast::transmute(&inner); let p : *RustClosure = ::cast::transmute(&inner);
ProtectBlock { cond: self, ProtectBlock { cond: self,
inner: *p } } inner: *p }
}
} }
fn raise<T, U: Copy>(t:&T) -> U { fn raise<T, U: Copy>(t:&T) -> U {
unsafe { unsafe {
match task::local_data::local_data_get(self.key) { match task::local_data::local_data_get(self.key) {
None => fail, None => {
debug!("Condition.raise: found no handler");
fail
}
Some(handler) => { Some(handler) => {
io::println("got handler"); debug!("Condition.raise: found handler");
let f : &fn(&T) -> U = ::cast::transmute(handler.handle); let f : &fn(&T) -> U = ::cast::transmute(handler.handle);
f(t) f(t)
} }
@ -79,39 +99,81 @@ impl Condition {
} }
#[test] #[cfg(test)]
fn happiness_key(_x: @Handler) { } fn happiness_key(_x: @Handler) { }
#[test] #[cfg(test)]
fn sadness_key(_x: @Handler) { } fn sadness_key(_x: @Handler) { }
#[test] #[cfg(test)]
fn trouble(i: int) { fn trouble(i: int) {
// Condition should work as a const, just limitations in consts. // Condition should work as a const, just limitations in consts.
let sadness_condition : Condition = Condition { key: sadness_key }; let sadness_condition : Condition = Condition { key: sadness_key };
io::println("raising"); debug!("trouble: raising conition");
let j = sadness_condition.raise(&i); let j = sadness_condition.raise(&i);
io::println(fmt!("handler recovered with %d", j)); debug!("trouble: handler recovered with %d", j);
} }
#[test] #[test]
fn test() { fn test1() {
let sadness_condition : Condition = Condition { key: sadness_key }; let sadness_condition : Condition = Condition { key: sadness_key };
let mut i = 10; let mut i = 10;
let b = do sadness_condition.protect { let b = do sadness_condition.protect {
io::println("in protected block"); debug!("test1: in protected block");
trouble(1); trouble(1);
trouble(2); trouble(2);
trouble(3); trouble(3);
}; };
do b.handle |j| { do b.handle |j| {
debug!("test1: in handler");
i += *j; i += *j;
i i
}; };
assert i == 16; assert i == 16;
} }
#[cfg(test)]
fn nested_test_inner() {
let sadness_condition : Condition = Condition { key: sadness_key };
let mut inner_trapped = false;
let b = do sadness_condition.protect {
debug!("nested_test_inner: in protected block");
trouble(1);
};
do b.handle |_j:&int| {
debug!("nested_test_inner: in handler");
inner_trapped = true;
0
};
assert inner_trapped;
}
#[test]
fn nested_test_outer() {
let sadness_condition : Condition = Condition { key: sadness_key };
let mut outer_trapped = false;
let b = do sadness_condition.protect {
debug!("nested_test_outer: in protected block");
nested_test_inner();
trouble(1);
};
do b.handle |_j:&int| {
debug!("nested_test_outer: in handler");
outer_trapped = true;
0
};
assert outer_trapped;
}