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