1#![allow(clippy::missing_panics_doc)]
18
19use crate::utils::panic_safe;
30use std::ffi::{c_void, CString};
31use std::fmt;
32use std::time::Duration;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
50pub enum DispatchQoS {
51 Background = 0,
53 Utility = 1,
55 #[default]
57 Default = 2,
58 UserInitiated = 3,
60 UserInteractive = 4,
62}
63
64pub struct DispatchQueue {
77 ptr: *const c_void,
78}
79
80unsafe impl Send for DispatchQueue {}
85unsafe impl Sync for DispatchQueue {}
86
87impl DispatchQueue {
88 #[must_use]
108 pub fn new(label: &str, qos: DispatchQoS) -> Self {
109 let c_label = CString::new(label).expect("Label contains null byte");
110 let ptr = unsafe { crate::ffi::acf_dispatch_queue_create(c_label.as_ptr(), qos as i32) };
111 assert!(!ptr.is_null(), "Failed to create dispatch queue");
112 Self { ptr }
113 }
114
115 #[must_use]
119 pub const fn as_ptr(&self) -> *const c_void {
120 self.ptr
121 }
122
123 #[must_use]
124 const fn as_mut_ptr(&self) -> *mut c_void {
125 self.ptr.cast_mut()
126 }
127}
128
129crate::utils::retained::cf_retained!(
130 DispatchQueue,
131 field = ptr,
132 retain = crate::ffi::dispatch_queue_retain,
133 release = crate::ffi::dispatch_queue_release,
134 drop = unchecked,
135);
136
137impl fmt::Debug for DispatchQueue {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 f.debug_struct("DispatchQueue")
140 .field("ptr", &self.ptr)
141 .finish()
142 }
143}
144
145impl fmt::Display for DispatchQueue {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 write!(f, "DispatchQueue")
148 }
149}
150
151fn timeout_ms(timeout: Option<Duration>) -> i64 {
152 timeout.map_or(-1, |duration| {
153 i64::try_from(duration.as_millis()).unwrap_or(i64::MAX)
154 })
155}
156
157struct DispatchOnceTask {
158 site: &'static str,
159 work: Option<Box<dyn FnOnce() + Send + 'static>>,
160}
161
162struct DispatchApplyTask {
163 work: Box<dyn Fn(usize) + Send + Sync + 'static>,
164}
165
166extern "C" fn dispatch_once_trampoline(context: *mut c_void) {
167 if context.is_null() {
168 return;
169 }
170 let mut task = unsafe { Box::from_raw(context.cast::<DispatchOnceTask>()) };
171 if let Some(work) = task.work.take() {
172 panic_safe::catch_user_panic(task.site, work);
173 }
174}
175
176extern "C" fn dispatch_apply_trampoline(iteration: usize, context: *mut c_void) {
177 if context.is_null() {
178 return;
179 }
180 let task = unsafe { &*context.cast::<DispatchApplyTask>() };
181 panic_safe::catch_user_panic("dispatch_apply", || (task.work)(iteration));
182}
183
184pub fn dispatch_async<F>(queue: &DispatchQueue, work: F)
186where
187 F: FnOnce() + Send + 'static,
188{
189 let task = Box::new(DispatchOnceTask {
190 site: "dispatch_async",
191 work: Some(Box::new(work)),
192 });
193 unsafe {
194 crate::ffi::acf_dispatch_async_f(
195 queue.as_mut_ptr(),
196 Box::into_raw(task).cast(),
197 dispatch_once_trampoline,
198 );
199 }
200}
201
202pub fn dispatch_async_and_wait<F>(queue: &DispatchQueue, work: F)
204where
205 F: FnOnce() + Send + 'static,
206{
207 let task = Box::new(DispatchOnceTask {
208 site: "dispatch_async_and_wait",
209 work: Some(Box::new(work)),
210 });
211 unsafe {
212 crate::ffi::acf_dispatch_async_and_wait_f(
213 queue.as_mut_ptr(),
214 Box::into_raw(task).cast(),
215 dispatch_once_trampoline,
216 );
217 }
218}
219
220pub fn dispatch_apply<F>(iterations: usize, queue: &DispatchQueue, work: F)
222where
223 F: Fn(usize) + Send + Sync + 'static,
224{
225 if iterations == 0 {
226 return;
227 }
228 let task = Box::new(DispatchApplyTask {
229 work: Box::new(work),
230 });
231 let raw = Box::into_raw(task);
232 unsafe {
233 crate::ffi::acf_dispatch_apply_f(
234 iterations,
235 queue.as_mut_ptr(),
236 raw.cast(),
237 dispatch_apply_trampoline,
238 );
239 drop(Box::from_raw(raw));
240 }
241}
242
243#[derive(PartialEq, Eq, Hash)]
245pub struct DispatchGroup {
246 ptr: *mut c_void,
247}
248
249unsafe impl Send for DispatchGroup {}
252unsafe impl Sync for DispatchGroup {}
253
254impl DispatchGroup {
255 #[must_use]
257 pub fn new() -> Self {
258 let ptr = unsafe { crate::ffi::acf_dispatch_group_create() };
259 assert!(!ptr.is_null(), "failed to create DispatchGroup");
260 Self { ptr }
261 }
262
263 pub fn enter(&self) {
265 unsafe { crate::ffi::acf_dispatch_group_enter(self.ptr) };
266 }
267
268 pub fn leave(&self) {
270 unsafe { crate::ffi::acf_dispatch_group_leave(self.ptr) };
271 }
272
273 #[must_use]
275 pub fn wait(&self, timeout: Option<Duration>) -> bool {
276 unsafe { crate::ffi::acf_dispatch_group_wait(self.ptr, timeout_ms(timeout)) }
277 }
278}
279
280impl Default for DispatchGroup {
281 fn default() -> Self {
282 Self::new()
283 }
284}
285
286crate::utils::retained::cf_retained!(
287 DispatchGroup,
288 field = ptr,
289 retain = crate::ffi::acf_object_retain,
290 release = crate::ffi::acf_object_release,
291 drop = unchecked,
292);
293
294impl fmt::Debug for DispatchGroup {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 f.debug_struct("DispatchGroup")
297 .field("ptr", &self.ptr)
298 .finish()
299 }
300}
301
302#[derive(PartialEq, Eq, Hash)]
304pub struct DispatchSemaphore {
305 ptr: *mut c_void,
306}
307
308unsafe impl Send for DispatchSemaphore {}
311unsafe impl Sync for DispatchSemaphore {}
312
313impl DispatchSemaphore {
314 #[must_use]
316 pub fn new(value: i64) -> Self {
317 let ptr = unsafe { crate::ffi::acf_dispatch_semaphore_create(value) };
318 assert!(!ptr.is_null(), "failed to create DispatchSemaphore");
319 Self { ptr }
320 }
321
322 #[must_use]
324 pub fn signal(&self) -> i64 {
325 unsafe { crate::ffi::acf_dispatch_semaphore_signal(self.ptr) }
326 }
327
328 #[must_use]
330 pub fn wait(&self, timeout: Option<Duration>) -> bool {
331 unsafe { crate::ffi::acf_dispatch_semaphore_wait(self.ptr, timeout_ms(timeout)) }
332 }
333}
334
335crate::utils::retained::cf_retained!(
336 DispatchSemaphore,
337 field = ptr,
338 retain = crate::ffi::acf_object_retain,
339 release = crate::ffi::acf_object_release,
340 drop = unchecked,
341);
342
343impl fmt::Debug for DispatchSemaphore {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 f.debug_struct("DispatchSemaphore")
346 .field("ptr", &self.ptr)
347 .finish()
348 }
349}
350
351#[derive(PartialEq, Eq, Hash)]
353pub struct DispatchSource {
354 ptr: *mut c_void,
355}
356
357unsafe impl Send for DispatchSource {}
360unsafe impl Sync for DispatchSource {}
361
362impl DispatchSource {
363 #[must_use]
365 pub fn timer(interval: Duration, leeway: Duration) -> Self {
366 let interval_ms = u64::try_from(interval.as_millis()).unwrap_or(u64::MAX);
367 let leeway_ms = u64::try_from(leeway.as_millis()).unwrap_or(u64::MAX);
368 let ptr = unsafe { crate::ffi::acf_dispatch_source_timer_create(interval_ms, leeway_ms) };
369 assert!(!ptr.is_null(), "failed to create DispatchSource timer");
370 Self { ptr }
371 }
372
373 pub fn resume(&self) {
377 unsafe { crate::ffi::acf_dispatch_source_timer_resume(self.ptr) };
378 }
379
380 pub fn cancel(&self) {
385 unsafe { crate::ffi::acf_dispatch_source_timer_cancel(self.ptr) };
386 }
387
388 #[must_use]
390 pub fn fire_count(&self) -> u64 {
391 unsafe { crate::ffi::acf_dispatch_source_timer_fire_count(self.ptr) }
392 }
393}
394
395crate::utils::retained::cf_retained!(
396 DispatchSource,
397 field = ptr,
398 retain = crate::ffi::acf_object_retain,
399 release = crate::ffi::acf_object_release,
400 drop = unchecked,
401);
402
403impl fmt::Debug for DispatchSource {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 f.debug_struct("DispatchSource")
406 .field("ptr", &self.ptr)
407 .field("fire_count", &self.fire_count())
408 .finish()
409 }
410}