// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use task; // helper for transmutation, shown below. type RustClosure = (int,int); pub struct Handler { handle: RustClosure, prev: Option<@Handler>, } pub struct Condition { name: &static/str, key: task::local_data::LocalDataKey> } impl Condition { fn trap(&self, h: &self/fn(&T) ->U) -> Trap/&self { unsafe { let p : *RustClosure = ::cast::transmute(&h); let prev = task::local_data::local_data_get(self.key); let h = @Handler{handle: *p, prev: prev}; move Trap { cond: self, handler: h } } } fn raise(t:&T) -> U { do self.raise_default(t) { fail fmt!("Unhandled condition: %s: %?", self.name, t); } } fn raise_default(t:&T, default: fn() -> U) -> U { unsafe { match task::local_data::local_data_pop(self.key) { None => { debug!("Condition.raise: found no handler"); default() } Some(handler) => { debug!("Condition.raise: found handler"); match handler.prev { None => (), Some(hp) => task::local_data::local_data_set(self.key, hp) } let handle : &fn(&T) -> U = ::cast::transmute(handler.handle); let u = handle(t); task::local_data::local_data_set(self.key, handler); move u } } } } } struct Trap { cond: &Condition, handler: @Handler } impl Trap { fn in(&self, inner: &self/fn() -> V) -> V { unsafe { let _g = Guard { cond: self.cond }; debug!("Trap: pushing handler to TLS"); task::local_data::local_data_set(self.cond.key, self.handler); inner() } } } struct Guard { cond: &Condition, drop { unsafe { debug!("Guard: popping handler from TLS"); let curr = task::local_data::local_data_pop(self.cond.key); match curr { None => (), Some(h) => match h.prev { None => (), Some(hp) => { task::local_data::local_data_set(self.cond.key, hp) } } } } } } #[cfg(test)] mod test { condition! { sadness: int -> int; } fn trouble(i: int) { debug!("trouble: raising conition"); let j = sadness::cond.raise(&i); debug!("trouble: handler recovered with %d", j); } fn nested_trap_test_inner() { let mut inner_trapped = false; do sadness::cond.trap(|_j| { debug!("nested_trap_test_inner: in handler"); inner_trapped = true; 0 }).in { debug!("nested_trap_test_inner: in protected block"); trouble(1); } assert inner_trapped; } #[test] fn nested_trap_test_outer() { let mut outer_trapped = false; do sadness::cond.trap(|_j| { debug!("nested_trap_test_outer: in handler"); outer_trapped = true; 0 }).in { debug!("nested_guard_test_outer: in protected block"); nested_trap_test_inner(); trouble(1); } assert outer_trapped; } fn nested_reraise_trap_test_inner() { let mut inner_trapped = false; do sadness::cond.trap(|_j| { debug!("nested_reraise_trap_test_inner: in handler"); inner_trapped = true; let i = 10; debug!("nested_reraise_trap_test_inner: handler re-raising"); sadness::cond.raise(&i) }).in { debug!("nested_reraise_trap_test_inner: in protected block"); trouble(1); } assert inner_trapped; } #[test] fn nested_reraise_trap_test_outer() { let mut outer_trapped = false; do sadness::cond.trap(|_j| { debug!("nested_reraise_trap_test_outer: in handler"); outer_trapped = true; 0 }).in { debug!("nested_reraise_trap_test_outer: in protected block"); nested_reraise_trap_test_inner(); } assert outer_trapped; } #[test] fn test_default() { let mut trapped = false; do sadness::cond.trap(|j| { debug!("test_default: in handler"); sadness::cond.raise_default(j, || {trapped=true; 5}) }).in { debug!("test_default: in protected block"); trouble(1); } assert trapped; } }