(index<- ) ./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 /*!
12
13 Condition handling
14
15 Conditions are a utility used to deal with handling error conditions. The syntax
16 of a condition handler strikes a resemblance to try/catch blocks in other
17 languages, but condition handlers are *not* a form of exception handling in the
18 same manner.
19
20 A condition is declared through the `condition!` macro provided by the compiler:
21
22 ```rust
23 condition! {
24 pub my_error: int -> ~str;
25 }
26 ```
27
28 This macro declares an inner module called `my_error` with one static variable,
29 `cond` that is a static `Condition` instance. To help understand what the other
30 parameters are used for, an example usage of this condition would be:
31
32 ```rust
33 do my_error::cond.trap(|raised_int| {
34
35 // the condition `my_error` was raised on, and the value it raised is stored
36 // in `raised_int`. This closure must return a `~str` type (as specified in
37 // the declaration of the condition
38 if raised_int == 3 { ~"three" } else { ~"oh well" }
39
40 }).inside {
41
42 // The condition handler above is installed for the duration of this block.
43 // That handler will override any previous handler, but the previous handler
44 // is restored when this block returns (handlers nest)
45 //
46 // If any code from this block (or code from another block) raises on the
47 // condition, then the above handler will be invoked (so long as there's no
48 // other nested handler).
49
50 println(my_error::cond.raise(3)); // prints "three"
51 println(my_error::cond.raise(4)); // prints "oh well"
52
53 }
54 ```
55
56 Condition handling is useful in cases where propagating errors is either to
57 cumbersome or just not necessary in the first place. It should also be noted,
58 though, that if there is not handler installed when a condition is raised, then
59 the task invokes `fail2!()` and will terminate.
60
61 ## More Info
62
63 Condition handlers as an error strategy is well explained in the [conditions
64 tutorial](http://static.rust-lang.org/doc/master/tutorial-conditions.html),
65 along with comparing and contrasting it with other error handling strategies.
66
67 */
68
69 use local_data;
70 use prelude::*;
71 use unstable::raw::Closure;
72
73 #[doc(hidden)]
74 pub struct Handler<T, U> {
75 priv handle: Closure,
76 priv prev: Option<@Handler<T, U>>,
77 }
78
79 /// This struct represents the state of a condition handler. It contains a key
80 /// into TLS which holds the currently install handler, along with the name of
81 /// the condition (useful for debugging).
82 ///
83 /// This struct should never be created directly, but rather only through the
84 /// `condition!` macro provided to all libraries using libstd.
85 pub struct Condition<T, U> {
86 /// Name of the condition handler
87 name: &'static str,
88 /// TLS key used to insert/remove values in TLS.
89 key: local_data::Key<@Handler<T, U>>
90 }
91
92 impl<T, U> Condition<T, U> {
93 /// Creates an object which binds the specified handler. This will also save
94 /// the current handler *on creation* such that when the `Trap` is consumed,
95 /// it knows which handler to restore.
96 ///
97 /// # Example
98 ///
99 /// ```rust
100 /// condition! { my_error: int -> int; }
101 ///
102 /// let trap = my_error::cond.trap(|error| error + 3);
103 ///
104 /// // use `trap`'s inside method to register the handler and then run a
105 /// // block of code with the handler registered
106 /// ```
107 pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> {
108 let h: Closure = unsafe { ::cast::transmute(h) };
109 let prev = local_data::get(self.key, |k| k.map(|x| *x));
110 let h = @Handler { handle: h, prev: prev };
111 Trap { cond: self, handler: h }
112 }
113
114 /// Raises on this condition, invoking any handler if one has been
115 /// registered, or failing the current task otherwise.
116 ///
117 /// While a condition handler is being run, the condition will have no
118 /// handler listed, so a task failure will occur if the condition is
119 /// re-raised during the handler.
120 ///
121 /// # Arguments
122 ///
123 /// * t - The argument to pass along to the condition handler.
124 ///
125 /// # Return value
126 ///
127 /// If a handler is found, its return value is returned, otherwise this
128 /// function will not return.
129 pub fn raise(&self, t: T) -> U {
130 let msg = format!("Unhandled condition: {}: {:?}", self.name, t);
131 self.raise_default(t, || fail2!("{}", msg.clone()))
132 }
133
134 /// Performs the same functionality as `raise`, except that when no handler
135 /// is found the `default` argument is called instead of failing the task.
136 pub fn raise_default(&self, t: T, default: &fn() -> U) -> U {
137 match local_data::pop(self.key) {
138 None => {
139 debug2!("Condition.raise: found no handler");
140 default()
141 }
142 Some(handler) => {
143 debug2!("Condition.raise: found handler");
144 match handler.prev {
145 None => {}
146 Some(hp) => local_data::set(self.key, hp)
147 }
148 let handle : &fn(T) -> U = unsafe {
149 ::cast::transmute(handler.handle)
150 };
151 let u = handle(t);
152 local_data::set(self.key, handler);
153 u
154 }
155 }
156 }
157 }
158
159 /// A `Trap` is created when the `trap` method is invoked on a `Condition`, and
160 /// it is used to actually bind a handler into the TLS slot reserved for this
161 /// condition.
162 ///
163 /// Normally this object is not dealt with directly, but rather it's directly
164 /// used after being returned from `trap`
165 struct Trap<'self, T, U> {
166 priv cond: &'self Condition<T, U>,
167 priv handler: @Handler<T, U>
168 }
169
170 impl<'self, T, U> Trap<'self, T, U> {
171 /// Execute a block of code with this trap handler's exception handler
172 /// registered.
173 ///
174 /// # Example
175 ///
176 /// ```rust
177 /// condition! { my_error: int -> int; }
178 ///
179 /// let result = do my_error::cond.trap(|error| error + 3).inside {
180 /// my_error::cond.raise(4)
181 /// };
182 /// assert_eq!(result, 7);
183 /// ```
184 pub fn inside<V>(&self, inner: &'self fn() -> V) -> V {
185 let _g = Guard { cond: self.cond };
186 debug2!("Trap: pushing handler to TLS");
187 local_data::set(self.cond.key, self.handler);
188 inner()
189 }
190 }
191
192 #[doc(hidden)]
193 struct Guard<'self, T, U> {
194 priv cond: &'self Condition<T, U>
195 }
196
197 #[unsafe_destructor]
198 impl<'self, T, U> Drop for Guard<'self, T, U> {
199 fn drop(&mut self) {
200 debug2!("Guard: popping handler from TLS");
201 let curr = local_data::pop(self.cond.key);
202 match curr {
203 None => {}
204 Some(h) => match h.prev {
205 None => {}
206 Some(hp) => local_data::set(self.cond.key, hp)
207 }
208 }
209 }
210 }
211
212 #[cfg(test)]
213 mod test {
214 condition! {
215 sadness: int -> int;
216 }
217
218 fn trouble(i: int) {
219 debug2!("trouble: raising condition");
220 let j = sadness::cond.raise(i);
221 debug2!("trouble: handler recovered with {}", j);
222 }
223
224 fn nested_trap_test_inner() {
225 let mut inner_trapped = false;
226
227 do sadness::cond.trap(|_j| {
228 debug2!("nested_trap_test_inner: in handler");
229 inner_trapped = true;
230 0
231 }).inside {
232 debug2!("nested_trap_test_inner: in protected block");
233 trouble(1);
234 }
235
236 assert!(inner_trapped);
237 }
238
239 #[test]
240 fn nested_trap_test_outer() {
241 let mut outer_trapped = false;
242
243 do sadness::cond.trap(|_j| {
244 debug2!("nested_trap_test_outer: in handler");
245 outer_trapped = true; 0
246 }).inside {
247 debug2!("nested_guard_test_outer: in protected block");
248 nested_trap_test_inner();
249 trouble(1);
250 }
251
252 assert!(outer_trapped);
253 }
254
255 fn nested_reraise_trap_test_inner() {
256 let mut inner_trapped = false;
257
258 do sadness::cond.trap(|_j| {
259 debug2!("nested_reraise_trap_test_inner: in handler");
260 inner_trapped = true;
261 let i = 10;
262 debug2!("nested_reraise_trap_test_inner: handler re-raising");
263 sadness::cond.raise(i)
264 }).inside {
265 debug2!("nested_reraise_trap_test_inner: in protected block");
266 trouble(1);
267 }
268
269 assert!(inner_trapped);
270 }
271
272 #[test]
273 fn nested_reraise_trap_test_outer() {
274 let mut outer_trapped = false;
275
276 do sadness::cond.trap(|_j| {
277 debug2!("nested_reraise_trap_test_outer: in handler");
278 outer_trapped = true; 0
279 }).inside {
280 debug2!("nested_reraise_trap_test_outer: in protected block");
281 nested_reraise_trap_test_inner();
282 }
283
284 assert!(outer_trapped);
285 }
286
287 #[test]
288 fn test_default() {
289 let mut trapped = false;
290
291 do sadness::cond.trap(|j| {
292 debug2!("test_default: in handler");
293 sadness::cond.raise_default(j, || { trapped=true; 5 })
294 }).inside {
295 debug2!("test_default: in protected block");
296 trouble(1);
297 }
298
299 assert!(trapped);
300 }
301
302 // Issue #6009
303 mod m {
304 condition! {
305 // #6009, #8215: should this truly need a `pub` for access from n?
306 pub sadness: int -> int;
307 }
308
309 mod n {
310 use super::sadness;
311
312 #[test]
313 fn test_conditions_are_public() {
314 let mut trapped = false;
315 do sadness::cond.trap(|_| {
316 trapped = true;
317 0
318 }).inside {
319 sadness::cond.raise(0);
320 }
321 assert!(trapped);
322 }
323 }
324 }
325 }
libstd/condition.rs:73:15-73:15 -struct- definition:
#[doc(hidden)]
pub struct Handler<T, U> {
references:-167: priv handler: @Handler<T, U>
89: key: local_data::Key<@Handler<T, U>>
110: let h = @Handler { handle: h, prev: prev };
76: priv prev: Option<@Handler<T, U>>,
<std-macros>:
191: local_data_key!(key: @::std::condition::Handler<$input, $out>)
191: local_data_key!(key: @::std::condition::Handler<$input, $out>)
191: local_data_key!(key: @::std::condition::Handler<$input, $out>)
191: local_data_key!(key: @::std::condition::Handler<$input, $out>)
210: local_data_key!(key: @::std::condition::Handler<$input, $out>)
191: local_data_key!(key: @::std::condition::Handler<$input, $out>)
libstd/condition.rs:164:42-164:42 -struct- definition:
/// used after being returned from `trap`
struct Trap<'self, T, U> {
references:-170: impl<'self, T, U> Trap<'self, T, U> {
107: pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> {
111: Trap { cond: self, handler: h }
libstd/condition.rs:192:15-192:15 -struct- definition:
#[doc(hidden)]
struct Guard<'self, T, U> {
references:-185: let _g = Guard { cond: self.cond };
198: impl<'self, T, U> Drop for Guard<'self, T, U> {
libstd/condition.rs:84:63-84:63 -struct- definition:
/// `condition!` macro provided to all libraries using libstd.
pub struct Condition<T, U> {
references:-92: impl<T, U> Condition<T, U> {
166: priv cond: &'self Condition<T, U>,
194: priv cond: &'self Condition<T, U>
<std-macros>:
194: ::std::condition::Condition<$input,$out> =
194: ::std::condition::Condition<$input,$out> =
213: ::std::condition::Condition<$input,$out> =
214: ::std::condition::Condition {
195: ::std::condition::Condition {
195: ::std::condition::Condition {
195: ::std::condition::Condition {
194: ::std::condition::Condition<$input,$out> =
195: ::std::condition::Condition {
194: ::std::condition::Condition<$input,$out> =
195: ::std::condition::Condition {
194: ::std::condition::Condition<$input,$out> =