(index<- )        ./librustuv/timer.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::mem;
  12  use std::rt::rtio::RtioTimer;
  13  use std::rt::task::BlockedTask;
  14  
  15  use homing::{HomeHandle, HomingIO};
  16  use super::{UvHandle, ForbidUnwind, ForbidSwitch, wait_until_woken_after, Loop};
  17  use uvio::UvIoFactory;
  18  use uvll;
  19  
  20  pub struct TimerWatcher {
  21      pub handle: *uvll::uv_timer_t,
  22      home: HomeHandle,
  23      action: Option<NextAction>,
  24      blocker: Option<BlockedTask>,
  25      id: uint, // see comments in timer_cb
  26  }
  27  
  28  pub enum NextAction {
  29      WakeTask,
  30      SendOnce(Sender<()>),
  31      SendMany(Sender<()>, uint),
  32  }
  33  
  34  impl TimerWatcher {
  35      pub fn new(io&mut UvIoFactory) -> Box<TimerWatcher> {
  36          let handle = io.make_handle();
  37          let me = box TimerWatcher::new_home(&io.loop_, handle);
  38          me.install()
  39      }
  40  
  41      pub fn new_home(loop_&Loop, homeHomeHandle) -> TimerWatcher {
  42          let handle = UvHandle::alloc(None::<TimerWatcher>, uvll::UV_TIMER);
  43          assert_eq!(unsafe { uvll::uv_timer_init(loop_.handle, handle) }, 0);
  44          TimerWatcher {
  45              handle: handle,
  46              action: None,
  47              blocker: None,
  48              home: home,
  49              id: 0,
  50          }
  51      }
  52  
  53      pub fn start(&mut self, fuvll::uv_timer_cb, msecsu64, periodu64) {
  54          assert_eq!(unsafe {
  55              uvll::uv_timer_start(self.handle, f, msecs, period)
  56          }, 0)
  57      }
  58  
  59      pub fn stop(&mut self) {
  60          assert_eq!(unsafe { uvll::uv_timer_stop(self.handle) }, 0)
  61      }
  62  
  63      pub unsafe fn set_data<T>(&mut self, data*T) {
  64          uvll::set_data_for_uv_handle(self.handle, data);
  65      }
  66  }
  67  
  68  impl HomingIO for TimerWatcher {
  69      fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
  70  }
  71  
  72  impl UvHandle<uvll::uv_timer_t> for TimerWatcher {
  73      fn uv_handle(&self) -> *uvll::uv_timer_t { self.handle }
  74  }
  75  
  76  impl RtioTimer for TimerWatcher {
  77      fn sleep(&mut self, msecsu64) {
  78          // As with all of the below functions, we must be extra careful when
  79          // destroying the previous action. If the previous action was a channel,
  80          // destroying it could invoke a context switch. For these situtations,
  81          // we must temporarily un-home ourselves, then destroy the action, and
  82          // then re-home again.
  83          let missile = self.fire_homing_missile();
  84          self.id += 1;
  85          self.stop();
  86          let _missile = match mem::replace(&mut self.action, None) {
  87              None => missile, // no need to do a homing dance
  88              Some(action) => {
  89                  drop(missile);      // un-home ourself
  90                  drop(action);       // destroy the previous action
  91                  self.fire_homing_missile()  // re-home ourself
  92              }
  93          };
  94  
  95          // If the descheduling operation unwinds after the timer has been
  96          // started, then we need to call stop on the timer.
  97          let _f = ForbidUnwind::new("timer");
  98  
  99          self.action = Some(WakeTask);
 100          wait_until_woken_after(&mut self.blocker, &self.uv_loop(), || {
 101              self.start(timer_cb, msecs, 0);
 102          });
 103          self.stop();
 104      }
 105  
 106      fn oneshot(&mut self, msecsu64) -> Receiver<()> {
 107          let (tx, rx) = channel();
 108  
 109          // similarly to the destructor, we must drop the previous action outside
 110          // of the homing missile
 111          let _prev_action = {
 112              let _m = self.fire_homing_missile();
 113              self.id += 1;
 114              self.stop();
 115              self.start(timer_cb, msecs, 0);
 116              mem::replace(&mut self.action, Some(SendOnce(tx)))
 117          };
 118  
 119          return rx;
 120      }
 121  
 122      fn period(&mut self, msecsu64) -> Receiver<()> {
 123          let (tx, rx) = channel();
 124  
 125          // similarly to the destructor, we must drop the previous action outside
 126          // of the homing missile
 127          let _prev_action = {
 128              let _m = self.fire_homing_missile();
 129              self.id += 1;
 130              self.stop();
 131              self.start(timer_cb, msecs, msecs);
 132              mem::replace(&mut self.action, Some(SendMany(tx, self.id)))
 133          };
 134  
 135          return rx;
 136      }
 137  }
 138  
 139  extern fn timer_cb(handle: *uvll::uv_timer_t) {
 140      let _f = ForbidSwitch::new("timer callback can't switch");
 141      let timer&mut TimerWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
 142  
 143      match timer.action.take_unwrap() {
 144          WakeTask => {
 145              let task = timer.blocker.take_unwrap();
 146              let _ = task.wake().map(|t| t.reawaken());
 147          }
 148          SendOnce(chan) => { let _ = chan.send_opt(()); }
 149          SendMany(chan, id) => {
 150              let _ = chan.send_opt(());
 151  
 152              // Note that the above operation could have performed some form of
 153              // scheduling. This means that the timer may have decided to insert
 154              // some other action to happen. This 'id' keeps track of the updates
 155              // to the timer, so we only reset the action back to sending on this
 156              // channel if the id has remained the same. This is essentially a
 157              // bug in that we have mutably aliasable memory, but that's libuv
 158              // for you. We're guaranteed to all be running on the same thread,
 159              // so there's no need for any synchronization here.
 160              if timer.id == id {
 161                  timer.action = Some(SendMany(chan, id));
 162              }
 163          }
 164      }
 165  }
 166  
 167  impl Drop for TimerWatcher {
 168      fn drop(&mut self) {
 169          // note that this drop is a little subtle. Dropping a channel which is
 170          // held internally may invoke some scheduling operations. We can't take
 171          // the channel unless we're on the home scheduler, but once we're on the
 172          // home scheduler we should never move. Hence, we take the timer's
 173          // action item and then move it outside of the homing block.
 174          let _action = {
 175              let _m = self.fire_homing_missile();
 176              self.stop();
 177              self.close();
 178              self.action.take()
 179          };
 180      }
 181  }
 182  
 183  #[cfg(test)]
 184  mod test {
 185      use std::rt::rtio::RtioTimer;
 186      use super::super::local_loop;
 187      use super::TimerWatcher;
 188  
 189      #[test]
 190      fn oneshot() {
 191          let mut timer = TimerWatcher::new(local_loop());
 192          let port = timer.oneshot(1);
 193          port.recv();
 194          let port = timer.oneshot(1);
 195          port.recv();
 196      }
 197  
 198      #[test]
 199      fn override() {
 200          let mut timer = TimerWatcher::new(local_loop());
 201          let oport = timer.oneshot(1);
 202          let pport = timer.period(1);
 203          timer.sleep(1);
 204          assert_eq!(oport.recv_opt(), Err(()));
 205          assert_eq!(pport.recv_opt(), Err(()));
 206          timer.oneshot(1).recv();
 207      }
 208  
 209      #[test]
 210      fn period() {
 211          let mut timer = TimerWatcher::new(local_loop());
 212          let port = timer.period(1);
 213          port.recv();
 214          port.recv();
 215          let port2 = timer.period(1);
 216          port2.recv();
 217          port2.recv();
 218      }
 219  
 220      #[test]
 221      fn sleep() {
 222          let mut timer = TimerWatcher::new(local_loop());
 223          timer.sleep(1);
 224          timer.sleep(1);
 225      }
 226  
 227      #[test] #[should_fail]
 228      fn oneshot_fail() {
 229          let mut timer = TimerWatcher::new(local_loop());
 230          let _port = timer.oneshot(1);
 231          fail!();
 232      }
 233  
 234      #[test] #[should_fail]
 235      fn period_fail() {
 236          let mut timer = TimerWatcher::new(local_loop());
 237          let _port = timer.period(1);
 238          fail!();
 239      }
 240  
 241      #[test] #[should_fail]
 242      fn normal_fail() {
 243          let _timer = TimerWatcher::new(local_loop());
 244          fail!();
 245      }
 246  
 247      #[test]
 248      fn closing_channel_during_drop_doesnt_kill_everything() {
 249          // see issue #10375
 250          let mut timer = TimerWatcher::new(local_loop());
 251          let timer_port = timer.period(1000);
 252  
 253          spawn(proc() {
 254              let _ = timer_port.recv_opt();
 255          });
 256  
 257          // when we drop the TimerWatcher we're going to destroy the channel,
 258          // which must wake up the task on the other end
 259      }
 260  
 261      #[test]
 262      fn reset_doesnt_switch_tasks() {
 263          // similar test to the one above.
 264          let mut timer = TimerWatcher::new(local_loop());
 265          let timer_port = timer.period(1000);
 266  
 267          spawn(proc() {
 268              let _ = timer_port.recv_opt();
 269          });
 270  
 271          drop(timer.oneshot(1));
 272      }
 273      #[test]
 274      fn reset_doesnt_switch_tasks2() {
 275          // similar test to the one above.
 276          let mut timer = TimerWatcher::new(local_loop());
 277          let timer_port = timer.period(1000);
 278  
 279          spawn(proc() {
 280              let _ = timer_port.recv_opt();
 281          });
 282  
 283          timer.sleep(1);
 284      }
 285  
 286      #[test]
 287      fn sender_goes_away_oneshot() {
 288          let port = {
 289              let mut timer = TimerWatcher::new(local_loop());
 290              timer.oneshot(1000)
 291          };
 292          assert_eq!(port.recv_opt(), Err(()));
 293      }
 294  
 295      #[test]
 296      fn sender_goes_away_period() {
 297          let port = {
 298              let mut timer = TimerWatcher::new(local_loop());
 299              timer.period(1000)
 300          };
 301          assert_eq!(port.recv_opt(), Err(()));
 302      }
 303  
 304      #[test]
 305      fn receiver_goes_away_oneshot() {
 306          let mut timer1 = TimerWatcher::new(local_loop());
 307          drop(timer1.oneshot(1));
 308          let mut timer2 = TimerWatcher::new(local_loop());
 309          // while sleeping, the prevous timer should fire and not have its
 310          // callback do something terrible.
 311          timer2.sleep(2);
 312      }
 313  
 314      #[test]
 315      fn receiver_goes_away_period() {
 316          let mut timer1 = TimerWatcher::new(local_loop());
 317          drop(timer1.period(1));
 318          let mut timer2 = TimerWatcher::new(local_loop());
 319          // while sleeping, the prevous timer should fire and not have its
 320          // callback do something terrible.
 321          timer2.sleep(2);
 322      }
 323  }


librustuv/timer.rs:19:1-19:1 -struct- definition:
pub struct TimerWatcher {
    pub handle: *uvll::uv_timer_t,
    home: HomeHandle,
references:- 13
43:         assert_eq!(unsafe { uvll::uv_timer_init(loop_.handle, handle) }, 0);
44:         TimerWatcher {
45:             handle: handle,
--
68: impl HomingIO for TimerWatcher {
69:     fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
--
140:     let _f = ForbidSwitch::new("timer callback can't switch");
141:     let timer: &mut TimerWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
--
167: impl Drop for TimerWatcher {
168:     fn drop(&mut self) {
librustuv/timeout.rs:
219: pub struct AcceptTimeout {
220:     timer: Option<TimerWatcher>,
221:     timeout_tx: Option<Sender<()>>,


librustuv/timer.rs:138:1-138:1 -fn- definition:
extern fn timer_cb(handle: *uvll::uv_timer_t) {
    let _f = ForbidSwitch::new("timer callback can't switch");
    let timer: &mut TimerWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
references:- 3
100:         wait_until_woken_after(&mut self.blocker, &self.uv_loop(), || {
101:             self.start(timer_cb, msecs, 0);
102:         });
--
114:             self.stop();
115:             self.start(timer_cb, msecs, 0);
116:             mem::replace(&mut self.action, Some(SendOnce(tx)))
--
130:             self.stop();
131:             self.start(timer_cb, msecs, msecs);
132:             mem::replace(&mut self.action, Some(SendMany(tx, self.id)))