about summary refs log tree commit diff
path: root/src/libcore/condition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcore/condition.rs')
-rw-r--r--src/libcore/condition.rs300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/libcore/condition.rs b/src/libcore/condition.rs
new file mode 100644
index 00000000000..77bd88e04d7
--- /dev/null
+++ b/src/libcore/condition.rs
@@ -0,0 +1,300 @@
+// helper for transmutation, shown below.
+type RustClosure = (int,int);
+
+struct Condition<T, U:Copy> {
+    key: task::local_data::LocalDataKey<Handler<T,U>>
+}
+
+struct Handler<T, U:Copy> {
+    handle: RustClosure
+}
+
+
+struct ProtectBlock<T, U:Copy> {
+    cond: &Condition<T, U>,
+    inner: RustClosure
+}
+
+struct Guard<T, U:Copy> {
+    cond: &Condition<T,U>,
+    prev: Option<@Handler<T, U>>,
+    drop {
+        match self.prev {
+            None => (),
+            Some(p) =>
+            unsafe {
+                debug!("Guard: popping handler from TLS");
+                task::local_data::local_data_set(self.cond.key, p)
+            }
+        }
+    }
+}
+
+struct HandleBlock<T, U:Copy> {
+    pb: &ProtectBlock<T,U>,
+    prev: Option<@Handler<T,U>>,
+    handler: @Handler<T,U>,
+    drop {
+        unsafe {
+            debug!("HandleBlock: pushing handler to TLS");
+            let _g = Guard { cond: self.pb.cond,
+                             prev: self.prev };
+            task::local_data::local_data_set(self.pb.cond.key,
+                                             self.handler);
+            // transmutation to avoid copying non-copyable, should
+            // be fixable by tracking closure pointees in regionck.
+            let f : &fn() = ::cast::transmute(self.pb.inner);
+            debug!("HandleBlock: invoking protected code");
+            f();
+            debug!("HandleBlock: returned from protected code");
+        }
+    }
+}
+
+struct Trap<T, U:Copy> {
+    cond: &Condition<T,U>,
+    handler: @Handler<T, U>
+}
+
+impl<T, U: Copy> ProtectBlock<T,U> {
+    fn handle(&self, h: &self/fn(&T) ->U) -> HandleBlock/&self<T,U> {
+        unsafe {
+            debug!("ProtectBlock.handle: setting up handler block");
+            let p : *RustClosure = ::cast::transmute(&h);
+            let prev = task::local_data::local_data_get(self.cond.key);
+            HandleBlock { pb: self,
+                          prev: prev,
+                          handler: @Handler{handle: *p} }
+        }
+    }
+}
+
+
+
+impl<T, U: Copy> Trap<T,U> {
+    fn in<V: Copy>(&self, inner: &self/fn() -> V) -> V {
+        unsafe {
+            let prev = task::local_data::local_data_get(self.cond.key);
+            let _g = Guard { cond: self.cond,
+                             prev: prev };
+            debug!("Trap: pushing handler to TLS");
+            task::local_data::local_data_set(self.cond.key, self.handler);
+            inner()
+        }
+    }
+}
+
+impl<T, U: Copy>  Condition<T,U> {
+
+    fn guard(&self, h: &self/fn(&T) ->U) -> Guard/&self<T,U> {
+        unsafe {
+            let prev = task::local_data::local_data_get(self.key);
+            let g = Guard { cond: self, prev: prev };
+            debug!("Guard: pushing handler to TLS");
+            let p : *RustClosure = ::cast::transmute(&h);
+            let h = @Handler{handle: *p};
+            task::local_data::local_data_set(self.key, h);
+            move g
+        }
+    }
+
+    fn trap(&self, h: &self/fn(&T) ->U) -> Trap/&self<T,U> {
+        unsafe {
+            let p : *RustClosure = ::cast::transmute(&h);
+            let h = @Handler{handle: *p};
+            move Trap { cond: self, handler: h }
+        }
+    }
+
+    fn protect(&self, inner: &self/fn()) -> ProtectBlock/&self<T,U> {
+        unsafe {
+            // transmutation to avoid copying non-copyable, should
+            // be fixable by tracking closure pointees in regionck.
+            debug!("Condition.protect: setting up protected block");
+            let p : *RustClosure = ::cast::transmute(&inner);
+            ProtectBlock { cond: self,
+                           inner: *p }
+        }
+    }
+
+    fn raise(t:&T) -> U {
+        unsafe {
+            match task::local_data::local_data_get(self.key) {
+                None => {
+                    debug!("Condition.raise: found no handler");
+                    fail
+                }
+
+                Some(handler) => {
+                    debug!("Condition.raise: found handler");
+                    let f : &fn(&T) -> U = ::cast::transmute(handler.handle);
+                    f(t)
+                }
+            }
+        }
+    }
+}
+
+
+#[cfg(test)]
+fn sadness_key(_x: @Handler<int,int>) { }
+
+#[cfg(test)]
+fn trouble(i: int) {
+    // Condition should work as a const, just limitations in consts.
+    let sadness_condition : Condition<int,int> =
+        Condition { key: sadness_key };
+    debug!("trouble: raising conition");
+    let j = sadness_condition.raise(&i);
+    debug!("trouble: handler recovered with %d", j);
+}
+
+#[test]
+fn test1() {
+
+    let sadness_condition : Condition<int,int> =
+        Condition { key: sadness_key };
+
+    let mut i = 10;
+
+    let b = do sadness_condition.protect {
+        debug!("test1: in protected block");
+        trouble(1);
+        trouble(2);
+        trouble(3);
+    };
+
+    do b.handle |j| {
+        debug!("test1: in handler");
+        i += *j;
+        i
+    };
+
+    assert i == 16;
+}
+#[cfg(test)]
+fn nested_test_inner() {
+    let sadness_condition : Condition<int,int> =
+        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| {
+        debug!("nested_test_inner: in handler");
+        inner_trapped = true;
+        0
+    };
+
+    assert inner_trapped;
+}
+
+#[test]
+fn nested_test_outer() {
+
+    let sadness_condition : Condition<int,int> =
+        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| {
+        debug!("nested_test_outer: in handler");
+        outer_trapped = true;
+        0
+    };
+
+    assert outer_trapped;
+}
+
+
+#[cfg(test)]
+fn nested_guard_test_inner() {
+    let sadness_condition : Condition<int,int> =
+        Condition { key: sadness_key };
+
+    let mut inner_trapped = false;
+
+    let _g = do sadness_condition.guard |_j| {
+        debug!("nested_guard_test_inner: in handler");
+        inner_trapped = true;
+        0
+    };
+
+    debug!("nested_guard_test_inner: in protected block");
+    trouble(1);
+
+    assert inner_trapped;
+}
+
+#[test]
+fn nested_guard_test_outer() {
+
+    let sadness_condition : Condition<int,int> =
+        Condition { key: sadness_key };
+
+    let mut outer_trapped = false;
+
+    let _g = do sadness_condition.guard |_j| {
+        debug!("nested_guard_test_outer: in handler");
+        outer_trapped = true;
+        0
+    };
+
+    debug!("nested_guard_test_outer: in protected block");
+    nested_guard_test_inner();
+    trouble(1);
+
+    assert outer_trapped;
+}
+
+
+
+#[cfg(test)]
+fn nested_trap_test_inner() {
+    let sadness_condition : Condition<int,int> =
+        Condition { key: sadness_key };
+
+    let mut inner_trapped = false;
+
+    do sadness_condition.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 sadness_condition : Condition<int,int> =
+        Condition { key: sadness_key };
+
+    let mut outer_trapped = false;
+
+    do sadness_condition.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;
+}