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