]> git.lizzy.rs Git - rust.git/blob - src/libnative/io/timer_win32.rs
9be09c6de076284ba5a4233425236e1aa1c4c289
[rust.git] / src / libnative / io / timer_win32.rs
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 use std::rt::rtio::{IoResult, Callback};
27 use std::comm;
28
29 use io::helper_thread::Helper;
30
31 helper_init!(static mut HELPER: Helper<Req>)
32
33 pub struct Timer {
34     obj: libc::HANDLE,
35     on_worker: bool,
36 }
37
38 pub enum Req {
39     NewTimer(libc::HANDLE, Box<Callback + Send>, bool),
40     RemoveTimer(libc::HANDLE, Sender<()>),
41 }
42
43 fn helper(input: libc::HANDLE, messages: Receiver<Req>, _: ()) {
44     let mut objs = vec![input];
45     let mut chans = vec![];
46
47     'outer: loop {
48         let idx = unsafe {
49             imp::WaitForMultipleObjects(objs.len() as libc::DWORD,
50                                         objs.as_ptr(),
51                                         0 as libc::BOOL,
52                                         libc::INFINITE)
53         };
54
55         if idx == 0 {
56             loop {
57                 match messages.try_recv() {
58                     Ok(NewTimer(obj, c, one)) => {
59                         objs.push(obj);
60                         chans.push((c, one));
61                     }
62                     Ok(RemoveTimer(obj, c)) => {
63                         c.send(());
64                         match objs.iter().position(|&o| o == obj) {
65                             Some(i) => {
66                                 drop(objs.remove(i));
67                                 drop(chans.remove(i - 1));
68                             }
69                             None => {}
70                         }
71                     }
72                     Err(comm::Disconnected) => {
73                         assert_eq!(objs.len(), 1);
74                         assert_eq!(chans.len(), 0);
75                         break 'outer;
76                     }
77                     Err(..) => break
78                 }
79             }
80         } else {
81             let remove = {
82                 match chans.get_mut(idx as uint - 1) {
83                     &(ref mut c, oneshot) => { c.call(); oneshot }
84                 }
85             };
86             if remove {
87                 drop(objs.remove(idx as uint));
88                 drop(chans.remove(idx as uint - 1));
89             }
90         }
91     }
92 }
93
94 // returns the current time (in milliseconds)
95 pub fn now() -> u64 {
96     let mut ticks_per_s = 0;
97     assert_eq!(unsafe { libc::QueryPerformanceFrequency(&mut ticks_per_s) }, 1);
98     let ticks_per_s = if ticks_per_s == 0 {1} else {ticks_per_s};
99     let mut ticks = 0;
100     assert_eq!(unsafe { libc::QueryPerformanceCounter(&mut ticks) }, 1);
101
102     return (ticks as u64 * 1000) / (ticks_per_s as u64);
103 }
104
105 impl Timer {
106     pub fn new() -> IoResult<Timer> {
107         unsafe { HELPER.boot(|| {}, helper) }
108
109         let obj = unsafe {
110             imp::CreateWaitableTimerA(ptr::mut_null(), 0, ptr::null())
111         };
112         if obj.is_null() {
113             Err(super::last_error())
114         } else {
115             Ok(Timer { obj: obj, on_worker: false, })
116         }
117     }
118
119     pub fn sleep(ms: u64) {
120         use std::rt::rtio::RtioTimer;
121         let mut t = Timer::new().ok().expect("must allocate a timer!");
122         t.sleep(ms);
123     }
124
125     fn remove(&mut self) {
126         if !self.on_worker { return }
127
128         let (tx, rx) = channel();
129         unsafe { HELPER.send(RemoveTimer(self.obj, tx)) }
130         rx.recv();
131
132         self.on_worker = false;
133     }
134 }
135
136 impl rtio::RtioTimer for Timer {
137     fn sleep(&mut self, msecs: u64) {
138         self.remove();
139
140         // there are 10^6 nanoseconds in a millisecond, and the parameter is in
141         // 100ns intervals, so we multiply by 10^4.
142         let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
143         assert_eq!(unsafe {
144             imp::SetWaitableTimer(self.obj, &due, 0, ptr::null(),
145                                   ptr::mut_null(), 0)
146         }, 1);
147
148         let _ = unsafe { imp::WaitForSingleObject(self.obj, libc::INFINITE) };
149     }
150
151     fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
152         self.remove();
153
154         // see above for the calculation
155         let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
156         assert_eq!(unsafe {
157             imp::SetWaitableTimer(self.obj, &due, 0, ptr::null(),
158                                   ptr::mut_null(), 0)
159         }, 1);
160
161         unsafe { HELPER.send(NewTimer(self.obj, cb, true)) }
162         self.on_worker = true;
163     }
164
165     fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
166         self.remove();
167
168         // see above for the calculation
169         let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
170         assert_eq!(unsafe {
171             imp::SetWaitableTimer(self.obj, &due, msecs as libc::LONG,
172                                   ptr::null(), ptr::mut_null(), 0)
173         }, 1);
174
175         unsafe { HELPER.send(NewTimer(self.obj, cb, false)) }
176         self.on_worker = true;
177     }
178 }
179
180 impl Drop for Timer {
181     fn drop(&mut self) {
182         self.remove();
183         assert!(unsafe { libc::CloseHandle(self.obj) != 0 });
184     }
185 }
186
187 mod imp {
188     use libc::{LPSECURITY_ATTRIBUTES, BOOL, LPCSTR, HANDLE, LARGE_INTEGER,
189                     LONG, LPVOID, DWORD, c_void};
190
191     pub type PTIMERAPCROUTINE = *c_void;
192
193     extern "system" {
194         pub fn CreateWaitableTimerA(lpTimerAttributes: LPSECURITY_ATTRIBUTES,
195                                     bManualReset: BOOL,
196                                     lpTimerName: LPCSTR) -> HANDLE;
197         pub fn SetWaitableTimer(hTimer: HANDLE,
198                                 pDueTime: *LARGE_INTEGER,
199                                 lPeriod: LONG,
200                                 pfnCompletionRoutine: PTIMERAPCROUTINE,
201                                 lpArgToCompletionRoutine: LPVOID,
202                                 fResume: BOOL) -> BOOL;
203         pub fn WaitForMultipleObjects(nCount: DWORD,
204                                       lpHandles: *HANDLE,
205                                       bWaitAll: BOOL,
206                                       dwMilliseconds: DWORD) -> DWORD;
207         pub fn WaitForSingleObject(hHandle: HANDLE,
208                                    dwMilliseconds: DWORD) -> DWORD;
209     }
210 }