(index<- )        ./libnative/io/timer_win32.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  //! Timers based on win32 WaitableTimers
  12  //!
  13  //! This implementation is meant to be used solely on windows. As with other
  14  //! implementations, there is a worker thread which is doing all the waiting on
  15  //! a large number of timers for all active timers in the system. This worker
  16  //! thread uses the select() equivalent, WaitForMultipleObjects. One of the
  17  //! objects being waited on is a signal into the worker thread to notify that
  18  //! the incoming channel should be looked at.
  19  //!
  20  //! Other than that, the implementation is pretty straightforward in terms of
  21  //! the other two implementations of timers with nothing *that* new showing up.
  22  
  23  use libc;
  24  use std::ptr;
  25  use std::rt::rtio;
  26  
  27  use io::timer_helper;
  28  use io::IoResult;
  29  
  30  pub struct Timer {
  31      obj: libc::HANDLE,
  32      on_worker: bool,
  33  }
  34  
  35  pub enum Req {
  36      NewTimer(libc::HANDLE, Sender<()>, bool),
  37      RemoveTimer(libc::HANDLE, Sender<()>),
  38      Shutdown,
  39  }
  40  
  41  fn helper(input: libc::HANDLE, messages: Receiver<Req>) {
  42      let mut objs = vec![input];
  43      let mut chans = vec![];
  44  
  45      'outer: loop {
  46          let idx = unsafe {
  47              imp::WaitForMultipleObjects(objs.len() as libc::DWORD,
  48                                          objs.as_ptr(),
  49                                          0 as libc::BOOL,
  50                                          libc::INFINITE)
  51          };
  52  
  53          if idx == 0 {
  54              loop {
  55                  match messages.try_recv() {
  56                      Ok(NewTimer(obj, c, one)) => {
  57                          objs.push(obj);
  58                          chans.push((c, one));
  59                      }
  60                      Ok(RemoveTimer(obj, c)) => {
  61                          c.send(());
  62                          match objs.iter().position(|&o| o == obj) {
  63                              Some(i) => {
  64                                  drop(objs.remove(i));
  65                                  drop(chans.remove(i - 1));
  66                              }
  67                              None => {}
  68                          }
  69                      }
  70                      Ok(Shutdown) => {
  71                          assert_eq!(objs.len(), 1);
  72                          assert_eq!(chans.len(), 0);
  73                          break 'outer;
  74                      }
  75                      _ => break
  76                  }
  77              }
  78          } else {
  79              let remove = {
  80                  match chans.get(idx as uint - 1) {
  81                      &(ref c, oneshot) => c.send_opt(()).is_err() || oneshot
  82                  }
  83              };
  84              if remove {
  85                  drop(objs.remove(idx as uint));
  86                  drop(chans.remove(idx as uint - 1));
  87              }
  88          }
  89      }
  90  }
  91  
  92  // returns the current time (in milliseconds)
  93  pub fn now() -> u64 {
  94      let mut ticks_per_s = 0;
  95      assert_eq!(unsafe { libc::QueryPerformanceFrequency(&mut ticks_per_s) }, 1);
  96      let ticks_per_s = if ticks_per_s == 0 {1} else {ticks_per_s};
  97      let mut ticks = 0;
  98      assert_eq!(unsafe { libc::QueryPerformanceCounter(&mut ticks) }, 1);
  99  
 100      return (ticks as u64 * 1000) / (ticks_per_s as u64);
 101  }
 102  
 103  impl Timer {
 104      pub fn new() -> IoResult<Timer> {
 105          timer_helper::boot(helper);
 106  
 107          let obj = unsafe {
 108              imp::CreateWaitableTimerA(ptr::mut_null(), 0, ptr::null())
 109          };
 110          if obj.is_null() {
 111              Err(super::last_error())
 112          } else {
 113              Ok(Timer { obj: obj, on_worker: false, })
 114          }
 115      }
 116  
 117      pub fn sleep(ms: u64) {
 118          use std::rt::rtio::RtioTimer;
 119          let mut t = Timer::new().ok().expect("must allocate a timer!");
 120          t.sleep(ms);
 121      }
 122  
 123      fn remove(&mut self) {
 124          if !self.on_worker { return }
 125  
 126          let (tx, rx) = channel();
 127          timer_helper::send(RemoveTimer(self.obj, tx));
 128          rx.recv();
 129  
 130          self.on_worker = false;
 131      }
 132  }
 133  
 134  impl rtio::RtioTimer for Timer {
 135      fn sleep(&mut self, msecs: u64) {
 136          self.remove();
 137  
 138          // there are 10^6 nanoseconds in a millisecond, and the parameter is in
 139          // 100ns intervals, so we multiply by 10^4.
 140          let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
 141          assert_eq!(unsafe {
 142              imp::SetWaitableTimer(self.obj, &due, 0, ptr::null(),
 143                                    ptr::mut_null(), 0)
 144          }, 1);
 145  
 146          let _ = unsafe { imp::WaitForSingleObject(self.obj, libc::INFINITE) };
 147      }
 148  
 149      fn oneshot(&mut self, msecs: u64) -> Receiver<(){
 150          self.remove();
 151          let (tx, rx) = channel();
 152  
 153          // see above for the calculation
 154          let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
 155          assert_eq!(unsafe {
 156              imp::SetWaitableTimer(self.obj, &due, 0, ptr::null(),
 157                                    ptr::mut_null(), 0)
 158          }, 1);
 159  
 160          timer_helper::send(NewTimer(self.obj, tx, true));
 161          self.on_worker = true;
 162          return rx;
 163      }
 164  
 165      fn period(&mut self, msecs: u64) -> Receiver<(){
 166          self.remove();
 167          let (tx, rx) = channel();
 168  
 169          // see above for the calculation
 170          let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
 171          assert_eq!(unsafe {
 172              imp::SetWaitableTimer(self.obj, &due, msecs as libc::LONG,
 173                                    ptr::null(), ptr::mut_null(), 0)
 174          }, 1);
 175  
 176          timer_helper::send(NewTimer(self.obj, tx, false));
 177          self.on_worker = true;
 178  
 179          return rx;
 180      }
 181  }
 182  
 183  impl Drop for Timer {
 184      fn drop(&mut self) {
 185          self.remove();
 186          assert!(unsafe { libc::CloseHandle(self.obj) != 0 });
 187      }
 188  }
 189  
 190  mod imp {
 191      use libc::{LPSECURITY_ATTRIBUTES, BOOL, LPCSTR, HANDLE, LARGE_INTEGER,
 192                      LONG, LPVOID, DWORD, c_void};
 193  
 194      pub type PTIMERAPCROUTINE = *c_void;
 195  
 196      extern "system" {
 197          pub fn CreateWaitableTimerA(lpTimerAttributes: LPSECURITY_ATTRIBUTES,
 198                                      bManualReset: BOOL,
 199                                      lpTimerName: LPCSTR) -> HANDLE;
 200          pub fn SetWaitableTimer(hTimer: HANDLE,
 201                                  pDueTime: *LARGE_INTEGER,
 202                                  lPeriod: LONG,
 203                                  pfnCompletionRoutine: PTIMERAPCROUTINE,
 204                                  lpArgToCompletionRoutine: LPVOID,
 205                                  fResume: BOOL) -> BOOL;
 206          pub fn WaitForMultipleObjects(nCount: DWORD,
 207                                        lpHandles: *HANDLE,
 208                                        bWaitAll: BOOL,
 209                                        dwMilliseconds: DWORD) -> DWORD;
 210          pub fn WaitForSingleObject(hHandle: HANDLE,
 211                                     dwMilliseconds: DWORD) -> DWORD;
 212      }
 213  }