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 }