async_timer/oneshot/
win.rs

1//! Windows API based timer
2
3use 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
55///Windows Native timer
56pub 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 {}