async_timer/oneshot/
win.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Windows API based timer

use super::state::TimerState;
use crate::alloc::boxed::Box;

use core::{mem, task, time, ptr};
use core::pin::Pin;
use core::future::Future;

mod ffi {
    pub use winapi::shared::minwindef::{FILETIME};
    pub use winapi::um::threadpoolapiset::{
        CloseThreadpoolTimer,
        CreateThreadpoolTimer,
        SetThreadpoolTimerEx,
        WaitForThreadpoolTimerCallbacks,
    };

    pub use winapi::ctypes::{c_ulong, c_void};
    pub use winapi::um::winnt::{PTP_TIMER_CALLBACK, PTP_CALLBACK_INSTANCE, PTP_TIMER};
}

unsafe extern "system" fn timer_callback(_: ffi::PTP_CALLBACK_INSTANCE, data: *mut ffi::c_void, _: ffi::PTP_TIMER) {
    #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
    let state = data as *mut TimerState;

    (*state).wake();
}

fn time_create(state: *mut TimerState) -> ffi::PTP_TIMER {
    let timer = unsafe {
        ffi::CreateThreadpoolTimer(Some(timer_callback), state as *mut ffi::c_void, ptr::null_mut())
    };
    os_assert!(!timer.is_null());

    timer
}

fn set_timer_value(fd: ffi::PTP_TIMER, timeout: time::Duration) {
    let mut ticks = i64::from(timeout.subsec_nanos() / 100);
    ticks += (timeout.as_secs() * 10_000_000) as i64;
    let ticks = -ticks;

    unsafe {
        let mut time: ffi::FILETIME = mem::transmute(ticks);
        ffi::SetThreadpoolTimerEx(fd, &mut time, 0, 0);
    }
}

enum State {
    Init(time::Duration),
    Running(ffi::PTP_TIMER, Box<TimerState>),
}

///Windows Native timer
pub struct WinTimer {
    state: State,
}

impl super::Oneshot for WinTimer {
    fn new(timeout: time::Duration) -> Self {
        debug_assert!(!(timeout.as_secs() == 0 && timeout.subsec_nanos() == 0), "Zero timeout makes no sense");

        Self {
            state: State::Init(timeout),
        }
    }

    fn is_ticking(&self) -> bool {
        match &self.state {
            State::Init(_) => false,
            State::Running(_, ref state) => !state.is_done(),
        }
    }

    fn is_expired(&self) -> bool {
        match &self.state {
            State::Init(_) => false,
            State::Running(_, ref state) => state.is_done(),
        }
    }

    fn cancel(&mut self) {
        match self.state {
            State::Init(_) => (),
            State::Running(fd, _) => unsafe {
                ffi::SetThreadpoolTimerEx(fd, ptr::null_mut(), 0, 0);
                ffi::WaitForThreadpoolTimerCallbacks(fd, 1);
            }
        }
    }

    fn restart(&mut self, new_value: time::Duration, waker: &task::Waker) {
        debug_assert!(!(new_value.as_secs() == 0 && new_value.subsec_nanos() == 0), "Zero timeout makes no sense");

        match &mut self.state {
            State::Init(ref mut timeout) => {
                *timeout = new_value;
            },
            State::Running(fd, ref mut state) => {
                state.register(waker);
                set_timer_value(*fd, new_value);
            }
        }
    }
}

impl Future for WinTimer {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
        self.state = match &self.state {
            State::Init(ref timeout) => {
                let state = Box::into_raw(Box::new(TimerState::new()));
                let fd = time_create(state);

                let state = unsafe { Box::from_raw(state) };
                state.register(ctx.waker());

                set_timer_value(fd, *timeout);

                State::Running(fd, state)
            },
            State::Running(_, ref state) => match state.is_done() {
                false => return task::Poll::Pending,
                true => return task::Poll::Ready(()),
            }
        };

        task::Poll::Pending
    }
}

impl Drop for WinTimer {
    fn drop(&mut self) {
        match self.state {
            State::Init(_) => (),
            State::Running(fd, _) => unsafe {
                ffi::SetThreadpoolTimerEx(fd, ptr::null_mut(), 0, 0);
                ffi::WaitForThreadpoolTimerCallbacks(fd, 1);
                ffi::CloseThreadpoolTimer(fd);
            }
        }
    }
}

unsafe impl Send for WinTimer {}
unsafe impl Sync for WinTimer {}