(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 hClosure = 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, tT) -> 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, tT, 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> =