async_timer/oneshot/
apple.rs1use core::{ptr, task, time};
4use core::pin::Pin;
5use core::future::Future;
6
7use super::state::TimerState;
8use crate::alloc::boxed::Box;
9
10use libc::{c_long, c_ulong, c_void, uintptr_t};
11
12#[allow(non_camel_case_types)]
13mod ffi {
14 use super::*;
15
16 pub type dispatch_object_t = *const c_void;
17 pub type dispatch_queue_t = *const c_void;
18 pub type dispatch_source_t = *const c_void;
19 pub type dispatch_source_type_t = *const c_void;
20 pub type dispatch_time_t = u64;
21
22 pub const DISPATCH_TIME_FOREVER: dispatch_time_t = !0;
23 pub const QOS_CLASS_DEFAULT: c_long = 0x15;
25
26 extern "C" {
27 pub static _dispatch_source_type_timer: c_long;
28
29 pub fn dispatch_get_global_queue(identifier: c_long, flags: c_ulong) -> dispatch_queue_t;
30 pub fn dispatch_source_create(type_: dispatch_source_type_t, handle: uintptr_t, mask: c_ulong, queue: dispatch_queue_t) -> dispatch_source_t;
31 pub fn dispatch_source_set_timer(source: dispatch_source_t, start: dispatch_time_t, interval: u64, leeway: u64);
32 pub fn dispatch_source_set_event_handler_f(source: dispatch_source_t, handler: unsafe extern "C" fn(*mut c_void));
33 pub fn dispatch_set_context(object: dispatch_object_t, context: *mut c_void);
34 pub fn dispatch_resume(object: dispatch_object_t);
35 pub fn dispatch_suspend(object: dispatch_object_t);
36 pub fn dispatch_release(object: dispatch_object_t);
37 pub fn dispatch_source_cancel(object: dispatch_object_t);
38 pub fn dispatch_walltime(when: *const c_void, delta: i64) -> dispatch_time_t;
39 }
40}
41
42unsafe extern "C" fn timer_handler(context: *mut c_void) {
44 let state = context as *mut TimerState;
45
46 (*state).wake();
47}
48
49struct TimerHandle {
50 inner: ffi::dispatch_source_t,
51 s_count: u8,
53}
54
55impl Drop for TimerHandle {
56 fn drop(&mut self) {
57 unsafe {
58 ffi::dispatch_source_cancel(self.inner);
59
60 self.resume();
63
64 ffi::dispatch_release(self.inner);
65 }
66 }
67}
68
69impl TimerHandle {
70 fn new(state: *mut TimerState) -> Self {
71 let inner = unsafe {
72 let queue = ffi::dispatch_get_global_queue(ffi::QOS_CLASS_DEFAULT, 0);
73 ffi::dispatch_source_create(&ffi::_dispatch_source_type_timer as *const _ as ffi::dispatch_source_type_t, 0, 0, queue)
74 };
75
76 os_assert!(!inner.is_null());
77
78 unsafe {
79 ffi::dispatch_source_set_event_handler_f(inner, timer_handler);
80 ffi::dispatch_set_context(inner, state as *mut _);
81 }
82
83 Self {
84 inner,
85 s_count: 1,
87 }
88 }
89
90 fn suspend(&mut self) {
91 if self.s_count == 0 {
92 unsafe {
93 ffi::dispatch_suspend(self.inner);
94 }
95
96 self.s_count += 1;
97 }
98 }
99
100 fn resume(&mut self) {
101 while self.s_count > 0 {
102 unsafe {
103 ffi::dispatch_resume(self.inner)
104 }
105
106 self.s_count -= 1;
107 }
108 }
109
110 fn set_delay(&mut self, timeout: time::Duration) {
111 self.suspend();
112
113 unsafe {
114 let start = ffi::dispatch_walltime(ptr::null(), timeout.as_nanos() as i64);
115 ffi::dispatch_source_set_timer(self.inner, start, ffi::DISPATCH_TIME_FOREVER, 0);
116 }
117
118 self.resume();
119 }
120}
121
122
123enum State {
124 Init(time::Duration),
125 Running(TimerHandle, Box<TimerState>),
126}
127
128pub struct AppleTimer {
130 state: State,
131}
132
133impl super::Oneshot for AppleTimer {
134 fn new(timeout: time::Duration) -> Self {
135 debug_assert!(!(timeout.as_secs() == 0 && timeout.subsec_nanos() == 0), "Zero timeout makes no sense");
136
137 Self {
138 state: State::Init(timeout),
139 }
140 }
141
142 fn is_ticking(&self) -> bool {
143 match &self.state {
144 State::Init(_) => false,
145 State::Running(_, ref state) => !state.is_done(),
146 }
147 }
148
149 fn is_expired(&self) -> bool {
150 match &self.state {
151 State::Init(_) => false,
152 State::Running(_, ref state) => state.is_done(),
153 }
154 }
155
156 fn cancel(&mut self) {
157 match &mut self.state {
158 State::Init(_) => (),
159 State::Running(ref mut fd, _) => {
160 fd.suspend();
161 }
162 }
163 }
164
165 fn restart(&mut self, new_value: time::Duration, waker: &task::Waker) {
166 debug_assert!(!(new_value.as_secs() == 0 && new_value.subsec_nanos() == 0), "Zero timeout makes no sense");
167
168 match &mut self.state {
169 State::Init(ref mut timeout) => {
170 *timeout = new_value;
171 },
172 State::Running(ref mut fd, ref state) => {
173 state.register(waker);
174 fd.set_delay(new_value);
175 },
176 }
177 }
178}
179
180impl Future for AppleTimer {
181 type Output = ();
182
183 fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
184 self.state = match &self.state {
185 State::Init(ref timeout) => {
186 let state = Box::into_raw(Box::new(TimerState::new()));
187 let mut fd = TimerHandle::new(state);
188
189 let state = unsafe { Box::from_raw(state) };
190 state.register(ctx.waker());
191
192 fd.set_delay(*timeout);
193
194 State::Running(fd, state)
195 },
196 State::Running(_, ref state) => match state.is_done() {
197 false => return task::Poll::Pending,
198 true => return task::Poll::Ready(()),
199 }
200 };
201
202 task::Poll::Pending
203 }
204}
205
206unsafe impl Send for AppleTimer {}
207unsafe impl Sync for AppleTimer {}