]> git.lizzy.rs Git - rust.git/blob - src/libstd/condition.rs
switch Drop to `&mut self`
[rust.git] / src / libstd / condition.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*! Condition handling */
12
13 #[allow(missing_doc)];
14
15 use local_data;
16 use prelude::*;
17
18 // helper for transmutation, shown below.
19 type RustClosure = (int, int);
20
21 pub struct Handler<T, U> {
22     handle: RustClosure,
23     prev: Option<@Handler<T, U>>,
24 }
25
26 pub struct Condition<T, U> {
27     name: &'static str,
28     key: local_data::Key<@Handler<T, U>>
29 }
30
31 impl<T, U> Condition<T, U> {
32     pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> {
33         unsafe {
34             let p : *RustClosure = ::cast::transmute(&h);
35             let prev = local_data::get(self.key, |k| k.map(|&x| *x));
36             let h = @Handler { handle: *p, prev: prev };
37             Trap { cond: self, handler: h }
38         }
39     }
40
41     pub fn raise(&self, t: T) -> U {
42         let msg = fmt!("Unhandled condition: %s: %?", self.name, t);
43         self.raise_default(t, || fail!(msg.clone()))
44     }
45
46     pub fn raise_default(&self, t: T, default: &fn() -> U) -> U {
47         unsafe {
48             match local_data::pop(self.key) {
49                 None => {
50                     debug!("Condition.raise: found no handler");
51                     default()
52                 }
53                 Some(handler) => {
54                     debug!("Condition.raise: found handler");
55                     match handler.prev {
56                         None => {}
57                         Some(hp) => local_data::set(self.key, hp)
58                     }
59                     let handle : &fn(T) -> U =
60                         ::cast::transmute(handler.handle);
61                     let u = handle(t);
62                     local_data::set(self.key, handler);
63                     u
64                 }
65             }
66         }
67     }
68 }
69
70 struct Trap<'self, T, U> {
71     cond: &'self Condition<T, U>,
72     handler: @Handler<T, U>
73 }
74
75 impl<'self, T, U> Trap<'self, T, U> {
76     pub fn inside<V>(&self, inner: &'self fn() -> V) -> V {
77         let _g = Guard { cond: self.cond };
78         debug!("Trap: pushing handler to TLS");
79         local_data::set(self.cond.key, self.handler);
80         inner()
81     }
82 }
83
84 struct Guard<'self, T, U> {
85     cond: &'self Condition<T, U>
86 }
87
88 #[unsafe_destructor]
89 impl<'self, T, U> Drop for Guard<'self, T, U> {
90     fn drop(&mut self) {
91         debug!("Guard: popping handler from TLS");
92         let curr = local_data::pop(self.cond.key);
93         match curr {
94             None => {}
95             Some(h) => match h.prev {
96                 None => {}
97                 Some(hp) => local_data::set(self.cond.key, hp)
98             }
99         }
100     }
101 }
102
103 #[cfg(test)]
104 mod test {
105     condition! {
106         sadness: int -> int;
107     }
108
109     fn trouble(i: int) {
110         debug!("trouble: raising condition");
111         let j = sadness::cond.raise(i);
112         debug!("trouble: handler recovered with %d", j);
113     }
114
115     fn nested_trap_test_inner() {
116         let mut inner_trapped = false;
117
118         do sadness::cond.trap(|_j| {
119             debug!("nested_trap_test_inner: in handler");
120             inner_trapped = true;
121             0
122         }).inside {
123             debug!("nested_trap_test_inner: in protected block");
124             trouble(1);
125         }
126
127         assert!(inner_trapped);
128     }
129
130     #[test]
131     fn nested_trap_test_outer() {
132         let mut outer_trapped = false;
133
134         do sadness::cond.trap(|_j| {
135             debug!("nested_trap_test_outer: in handler");
136             outer_trapped = true; 0
137         }).inside {
138             debug!("nested_guard_test_outer: in protected block");
139             nested_trap_test_inner();
140             trouble(1);
141         }
142
143         assert!(outer_trapped);
144     }
145
146     fn nested_reraise_trap_test_inner() {
147         let mut inner_trapped = false;
148
149         do sadness::cond.trap(|_j| {
150             debug!("nested_reraise_trap_test_inner: in handler");
151             inner_trapped = true;
152             let i = 10;
153             debug!("nested_reraise_trap_test_inner: handler re-raising");
154             sadness::cond.raise(i)
155         }).inside {
156             debug!("nested_reraise_trap_test_inner: in protected block");
157             trouble(1);
158         }
159
160         assert!(inner_trapped);
161     }
162
163     #[test]
164     fn nested_reraise_trap_test_outer() {
165         let mut outer_trapped = false;
166
167         do sadness::cond.trap(|_j| {
168             debug!("nested_reraise_trap_test_outer: in handler");
169             outer_trapped = true; 0
170         }).inside {
171             debug!("nested_reraise_trap_test_outer: in protected block");
172             nested_reraise_trap_test_inner();
173         }
174
175         assert!(outer_trapped);
176     }
177
178     #[test]
179     fn test_default() {
180         let mut trapped = false;
181
182         do sadness::cond.trap(|j| {
183             debug!("test_default: in handler");
184             sadness::cond.raise_default(j, || { trapped=true; 5 })
185         }).inside {
186             debug!("test_default: in protected block");
187             trouble(1);
188         }
189
190         assert!(trapped);
191     }
192
193     // Issue #6009
194     mod m {
195         condition! {
196             // #6009, #8215: should this truly need a `pub` for access from n?
197             pub sadness: int -> int;
198         }
199
200         mod n {
201             use super::sadness;
202
203             #[test]
204             fn test_conditions_are_public() {
205                 let mut trapped = false;
206                 do sadness::cond.trap(|_| {
207                     trapped = true;
208                     0
209                 }).inside {
210                     sadness::cond.raise(0);
211                 }
212                 assert!(trapped);
213             }
214         }
215     }
216 }