async_timer/oneshot/
win.rs1use super::state::TimerState;
4use crate::alloc::boxed::Box;
5
6use core::{mem, task, time, ptr};
7use core::pin::Pin;
8use core::future::Future;
9
10mod ffi {
11 pub use winapi::shared::minwindef::{FILETIME};
12 pub use winapi::um::threadpoolapiset::{
13 CloseThreadpoolTimer,
14 CreateThreadpoolTimer,
15 SetThreadpoolTimerEx,
16 WaitForThreadpoolTimerCallbacks,
17 };
18
19 pub use winapi::ctypes::{c_ulong, c_void};
20 pub use winapi::um::winnt::{PTP_TIMER_CALLBACK, PTP_CALLBACK_INSTANCE, PTP_TIMER};
21}
22
23unsafe extern "system" fn timer_callback(_: ffi::PTP_CALLBACK_INSTANCE, data: *mut ffi::c_void, _: ffi::PTP_TIMER) {
24 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
25 let state = data as *mut TimerState;
26
27 (*state).wake();
28}
29
30fn time_create(state: *mut TimerState) -> ffi::PTP_TIMER {
31 let timer = unsafe {
32 ffi::CreateThreadpoolTimer(Some(timer_callback), state as *mut ffi::c_void, ptr::null_mut())
33 };
34 os_assert!(!timer.is_null());
35
36 timer
37}
38
39fn set_timer_value(fd: ffi::PTP_TIMER, timeout: time::Duration) {
40 let mut ticks = i64::from(timeout.subsec_nanos() / 100);
41 ticks += (timeout.as_secs() * 10_000_000) as i64;
42 let ticks = -ticks;
43
44 unsafe {
45 let mut time: ffi::FILETIME = mem::transmute(ticks);
46 ffi::SetThreadpoolTimerEx(fd, &mut time, 0, 0);
47 }
48}
49
50enum State {
51 Init(time::Duration),
52 Running(ffi::PTP_TIMER, Box<TimerState>),
53}
54
55pub struct WinTimer {
57 state: State,
58}
59
60impl super::Oneshot for WinTimer {
61 fn new(timeout: time::Duration) -> Self {
62 debug_assert!(!(timeout.as_secs() == 0 && timeout.subsec_nanos() == 0), "Zero timeout makes no sense");
63
64 Self {
65 state: State::Init(timeout),
66 }
67 }
68
69 fn is_ticking(&self) -> bool {
70 match &self.state {
71 State::Init(_) => false,
72 State::Running(_, ref state) => !state.is_done(),
73 }
74 }
75
76 fn is_expired(&self) -> bool {
77 match &self.state {
78 State::Init(_) => false,
79 State::Running(_, ref state) => state.is_done(),
80 }
81 }
82
83 fn cancel(&mut self) {
84 match self.state {
85 State::Init(_) => (),
86 State::Running(fd, _) => unsafe {
87 ffi::SetThreadpoolTimerEx(fd, ptr::null_mut(), 0, 0);
88 ffi::WaitForThreadpoolTimerCallbacks(fd, 1);
89 }
90 }
91 }
92
93 fn restart(&mut self, new_value: time::Duration, waker: &task::Waker) {
94 debug_assert!(!(new_value.as_secs() == 0 && new_value.subsec_nanos() == 0), "Zero timeout makes no sense");
95
96 match &mut self.state {
97 State::Init(ref mut timeout) => {
98 *timeout = new_value;
99 },
100 State::Running(fd, ref mut state) => {
101 state.register(waker);
102 set_timer_value(*fd, new_value);
103 }
104 }
105 }
106}
107
108impl Future for WinTimer {
109 type Output = ();
110
111 fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
112 self.state = match &self.state {
113 State::Init(ref timeout) => {
114 let state = Box::into_raw(Box::new(TimerState::new()));
115 let fd = time_create(state);
116
117 let state = unsafe { Box::from_raw(state) };
118 state.register(ctx.waker());
119
120 set_timer_value(fd, *timeout);
121
122 State::Running(fd, state)
123 },
124 State::Running(_, ref state) => match state.is_done() {
125 false => return task::Poll::Pending,
126 true => return task::Poll::Ready(()),
127 }
128 };
129
130 task::Poll::Pending
131 }
132}
133
134impl Drop for WinTimer {
135 fn drop(&mut self) {
136 match self.state {
137 State::Init(_) => (),
138 State::Running(fd, _) => unsafe {
139 ffi::SetThreadpoolTimerEx(fd, ptr::null_mut(), 0, 0);
140 ffi::WaitForThreadpoolTimerCallbacks(fd, 1);
141 ffi::CloseThreadpoolTimer(fd);
142 }
143 }
144 }
145}
146
147unsafe impl Send for WinTimer {}
148unsafe impl Sync for WinTimer {}