(index<- )        ./librustuv/idle.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2013 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  use std::cast;
  12  use libc::c_void;
  13  
  14  use uvll;
  15  use super::{Loop, UvHandle};
  16  use std::rt::rtio::{Callback, PausableIdleCallback};
  17  
  18  pub struct IdleWatcher {
  19      handle: *uvll::uv_idle_t,
  20      idle_flag: bool,
  21      closed: bool,
  22      callback: Box<Callback:Send>,
  23  }
  24  
  25  impl IdleWatcher {
  26      pub fn new(loop_&mut Loop, cbBox<Callback:Send>) -> Box<IdleWatcher> {
  27          let handle = UvHandle::alloc(None::<IdleWatcher>, uvll::UV_IDLE);
  28          assert_eq!(unsafe {
  29              uvll::uv_idle_init(loop_.handle, handle)
  30          }, 0);
  31          let me = box IdleWatcher {
  32              handle: handle,
  33              idle_flag: false,
  34              closed: false,
  35              callback: cb,
  36          };
  37          return me.install();
  38      }
  39  
  40      pub fn onetime(loop_&mut Loop, fproc()) {
  41          let handle = UvHandle::alloc(None::<IdleWatcher>, uvll::UV_IDLE);
  42          unsafe {
  43              assert_eq!(uvll::uv_idle_init(loop_.handle, handle), 0);
  44              let data*c_void = cast::transmute(box f);
  45              uvll::set_data_for_uv_handle(handle, data);
  46              assert_eq!(uvll::uv_idle_start(handle, onetime_cb), 0)
  47          }
  48  
  49          extern fn onetime_cb(handle: *uvll::uv_idle_t) {
  50              unsafe {
  51                  let data = uvll::get_data_for_uv_handle(handle);
  52                  let fBox<proc()> = cast::transmute(data);
  53                  (*f)();
  54                  assert_eq!(uvll::uv_idle_stop(handle), 0);
  55                  uvll::uv_close(handle, close_cb);
  56              }
  57          }
  58  
  59          extern fn close_cb(handle*uvll::uv_handle_t) {
  60              unsafe { uvll::free_handle(handle) }
  61          }
  62      }
  63  }
  64  
  65  impl PausableIdleCallback for IdleWatcher {
  66      fn pause(&mut self) {
  67          if self.idle_flag == true {
  68              assert_eq!(unsafe {uvll::uv_idle_stop(self.handle) }, 0);
  69              self.idle_flag = false;
  70          }
  71      }
  72      fn resume(&mut self) {
  73          if self.idle_flag == false {
  74              assert_eq!(unsafe { uvll::uv_idle_start(self.handle, idle_cb) }, 0)
  75              self.idle_flag = true;
  76          }
  77      }
  78  }
  79  
  80  impl UvHandle<uvll::uv_idle_t> for IdleWatcher {
  81      fn uv_handle(&self) -> *uvll::uv_idle_t { self.handle }
  82  }
  83  
  84  extern fn idle_cb(handle: *uvll::uv_idle_t) {
  85      let idle&mut IdleWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
  86      idle.callback.call();
  87  }
  88  
  89  impl Drop for IdleWatcher {
  90      fn drop(&mut self) {
  91          self.pause();
  92          self.close_async_();
  93      }
  94  }
  95  
  96  #[cfg(test)]
  97  mod test {
  98      use std::cast;
  99      use std::cell::RefCell;
 100      use std::rc::Rc;
 101      use std::rt::rtio::{Callback, PausableIdleCallback};
 102      use std::rt::task::{BlockedTask, Task};
 103      use std::rt::local::Local;
 104      use super::IdleWatcher;
 105      use super::super::local_loop;
 106  
 107      type Chan = Rc<RefCell<(Option<BlockedTask>, uint)>>;
 108  
 109      struct MyCallback(Rc<RefCell<(Option<BlockedTask>, uint)>>, uint);
 110      impl Callback for MyCallback {
 111          fn call(&mut self) {
 112              let task = match *self {
 113                  MyCallback(ref rc, n) => {
 114                      match *rc.borrow_mut().deref_mut() {
 115                          (ref mut task, ref mut val) => {
 116                              *val = n;
 117                              match task.take() {
 118                                  Some(t) => t,
 119                                  None => return
 120                              }
 121                          }
 122                      }
 123                  }
 124              };
 125              let _ = task.wake().map(|t| t.reawaken());
 126          }
 127      }
 128  
 129      fn mk(v: uint) -> (Box<IdleWatcher>, Chan) {
 130          let rc = Rc::new(RefCell::new((None, 0)));
 131          let cb = box MyCallback(rc.clone(), v);
 132          let cb = cb as Box<Callback:>;
 133          let cb = unsafe { cast::transmute(cb) };
 134          (IdleWatcher::new(&mut local_loop().loop_, cb), rc)
 135      }
 136  
 137      fn sleep(chan: &Chan) -> uint {
 138          let task: Box<Task> = Local::take();
 139          task.deschedule(1, |task| {
 140              match *chan.borrow_mut().deref_mut() {
 141                  (ref mut slot, _) => {
 142                      assert!(slot.is_none());
 143                      *slot = Some(task);
 144                  }
 145              }
 146              Ok(())
 147          });
 148  
 149          match *chan.borrow() { (_, n) => n }
 150      }
 151  
 152      #[test]
 153      fn not_used() {
 154          let (_idle, _chan) = mk(1);
 155      }
 156  
 157      #[test]
 158      fn smoke_test() {
 159          let (mut idle, chan) = mk(1);
 160          idle.resume();
 161          assert_eq!(sleep(&chan), 1);
 162      }
 163  
 164      #[test] #[should_fail]
 165      fn smoke_fail() {
 166          // By default, the test harness is capturing our stderr output through a
 167          // channel. This means that when we start failing and "print" our error
 168          // message, we could be switched to running on another test. The
 169          // IdleWatcher assumes that we're already running on the same task, so
 170          // it can cause serious problems and internal race conditions.
 171          //
 172          // To fix this bug, we just set our stderr to a null writer which will
 173          // never reschedule us, so we're guaranteed to stay on the same
 174          // task/event loop.
 175          use std::io;
 176          drop(io::stdio::set_stderr(box io::util::NullWriter));
 177  
 178          let (mut idle, _chan) = mk(1);
 179          idle.resume();
 180          fail!();
 181      }
 182  
 183      #[test]
 184      fn fun_combinations_of_methods() {
 185          let (mut idle, chan) = mk(1);
 186          idle.resume();
 187          assert_eq!(sleep(&chan), 1);
 188          idle.pause();
 189          idle.resume();
 190          idle.resume();
 191          assert_eq!(sleep(&chan), 1);
 192          idle.pause();
 193          idle.pause();
 194          idle.resume();
 195          assert_eq!(sleep(&chan), 1);
 196      }
 197  
 198      #[test]
 199      fn pause_pauses() {
 200          let (mut idle1, chan1) = mk(1);
 201          let (mut idle2, chan2) = mk(2);
 202          idle2.resume();
 203          assert_eq!(sleep(&chan2), 2);
 204          idle2.pause();
 205          idle1.resume();
 206          assert_eq!(sleep(&chan1), 1);
 207      }
 208  }