1#![allow(clippy::missing_panics_doc)]
18
19use crate::utils::panic_safe;
30use std::ffi::c_void;
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]
110 pub fn new(label: &str, qos: DispatchQoS) -> Self {
111 let c_label = crate::utils::ffi_string::cstring_until_nul(label);
112 let ptr = unsafe { crate::ffi::acf_dispatch_queue_create(c_label.as_ptr(), qos as i32) };
113 assert!(!ptr.is_null(), "Failed to create dispatch queue");
114 Self { ptr }
115 }
116
117 #[must_use]
118 pub fn concurrent(label: &str, qos: DispatchQoS) -> Self {
119 let c_label = crate::utils::ffi_string::cstring_until_nul(label);
120 let ptr = unsafe {
121 crate::ffi::acf_dispatch_queue_create_concurrent(c_label.as_ptr(), qos as i32)
122 };
123 assert!(!ptr.is_null(), "Failed to create dispatch queue");
124 Self { ptr }
125 }
126
127 #[must_use]
128 pub fn main() -> Self {
129 let ptr = unsafe { crate::ffi::acf_dispatch_queue_main() };
130 assert!(!ptr.is_null(), "dispatch main queue is NULL");
131 Self { ptr }
132 }
133
134 #[must_use]
135 pub fn global(qos: DispatchQoS) -> Self {
136 let ptr = unsafe { crate::ffi::acf_dispatch_queue_global(qos as i32) };
137 assert!(!ptr.is_null(), "dispatch global queue is NULL");
138 Self { ptr }
139 }
140
141 #[must_use]
145 pub const fn as_ptr(&self) -> *const c_void {
146 self.ptr
147 }
148
149 #[must_use]
150 const fn as_mut_ptr(&self) -> *mut c_void {
151 self.ptr.cast_mut()
152 }
153}
154
155crate::utils::retained::cf_retained!(
156 DispatchQueue,
157 field = ptr,
158 retain = crate::ffi::dispatch_queue_retain,
159 release = crate::ffi::dispatch_queue_release,
160 drop = unchecked,
161);
162
163impl fmt::Debug for DispatchQueue {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 f.debug_struct("DispatchQueue")
166 .field("ptr", &self.ptr)
167 .finish()
168 }
169}
170
171impl fmt::Display for DispatchQueue {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 write!(f, "DispatchQueue")
174 }
175}
176
177fn timeout_ms(timeout: Option<Duration>) -> i64 {
178 timeout.map_or(-1, |duration| {
179 i64::try_from(duration.as_millis()).unwrap_or(i64::MAX)
180 })
181}
182
183struct DispatchOnceTask {
184 site: &'static str,
185 work: Option<Box<dyn FnOnce() + Send + 'static>>,
186}
187
188struct DispatchApplyTask {
189 work: Box<dyn Fn(usize) + Send + Sync + 'static>,
190}
191
192extern "C" fn dispatch_once_trampoline(context: *mut c_void) {
193 if context.is_null() {
194 return;
195 }
196 let mut task = unsafe { Box::from_raw(context.cast::<DispatchOnceTask>()) };
197 if let Some(work) = task.work.take() {
198 panic_safe::catch_user_panic(task.site, work);
199 }
200}
201
202extern "C" fn dispatch_apply_trampoline(iteration: usize, context: *mut c_void) {
203 if context.is_null() {
204 return;
205 }
206 let task = unsafe { &*context.cast::<DispatchApplyTask>() };
207 panic_safe::catch_user_panic("dispatch_apply", || (task.work)(iteration));
208}
209
210pub fn dispatch_async<F>(queue: &DispatchQueue, work: F)
212where
213 F: FnOnce() + Send + 'static,
214{
215 let task = Box::new(DispatchOnceTask {
216 site: "dispatch_async",
217 work: Some(Box::new(work)),
218 });
219 unsafe {
220 crate::ffi::acf_dispatch_async_f(
221 queue.as_mut_ptr(),
222 Box::into_raw(task).cast(),
223 dispatch_once_trampoline,
224 );
225 }
226}
227
228pub fn dispatch_async_and_wait<F>(queue: &DispatchQueue, work: F)
230where
231 F: FnOnce() + Send + 'static,
232{
233 let task = Box::new(DispatchOnceTask {
234 site: "dispatch_async_and_wait",
235 work: Some(Box::new(work)),
236 });
237 unsafe {
238 crate::ffi::acf_dispatch_async_and_wait_f(
239 queue.as_mut_ptr(),
240 Box::into_raw(task).cast(),
241 dispatch_once_trampoline,
242 );
243 }
244}
245
246pub fn dispatch_after<F>(delay: Duration, queue: &DispatchQueue, work: F)
247where
248 F: FnOnce() + Send + 'static,
249{
250 let task = Box::new(DispatchOnceTask {
251 site: "dispatch_after",
252 work: Some(Box::new(work)),
253 });
254 let delay_ns = u64::try_from(delay.as_nanos()).unwrap_or(u64::MAX);
255 unsafe {
256 crate::ffi::acf_dispatch_after_f(
257 delay_ns,
258 queue.as_mut_ptr(),
259 Box::into_raw(task).cast(),
260 dispatch_once_trampoline,
261 );
262 }
263}
264
265pub fn dispatch_apply<F>(iterations: usize, queue: &DispatchQueue, work: F)
267where
268 F: Fn(usize) + Send + Sync + 'static,
269{
270 if iterations == 0 {
271 return;
272 }
273 let task = Box::new(DispatchApplyTask {
274 work: Box::new(work),
275 });
276 let raw = Box::into_raw(task);
277 unsafe {
278 crate::ffi::acf_dispatch_apply_f(
279 iterations,
280 queue.as_mut_ptr(),
281 raw.cast(),
282 dispatch_apply_trampoline,
283 );
284 drop(Box::from_raw(raw));
285 }
286}
287
288#[derive(PartialEq, Eq, Hash)]
290pub struct DispatchGroup {
291 ptr: *mut c_void,
292}
293
294unsafe impl Send for DispatchGroup {}
297unsafe impl Sync for DispatchGroup {}
298
299impl DispatchGroup {
300 #[must_use]
302 pub fn new() -> Self {
303 let ptr = unsafe { crate::ffi::acf_dispatch_group_holder_create() };
304 assert!(!ptr.is_null(), "failed to create DispatchGroup");
305 Self { ptr }
306 }
307
308 pub fn enter(&self) {
310 unsafe { crate::ffi::acf_dispatch_group_holder_enter(self.ptr) };
311 }
312
313 pub fn leave(&self) {
315 unsafe { crate::ffi::acf_dispatch_group_holder_leave(self.ptr) };
316 }
317
318 #[must_use]
320 pub fn wait(&self, timeout: Option<Duration>) -> bool {
321 unsafe { crate::ffi::acf_dispatch_group_holder_wait(self.ptr, timeout_ms(timeout)) }
322 }
323}
324
325impl Default for DispatchGroup {
326 fn default() -> Self {
327 Self::new()
328 }
329}
330
331crate::utils::retained::cf_retained!(
332 DispatchGroup,
333 field = ptr,
334 retain = crate::ffi::acf_object_retain,
335 release = crate::ffi::acf_object_release,
336 drop = unchecked,
337);
338
339impl fmt::Debug for DispatchGroup {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 f.debug_struct("DispatchGroup")
342 .field("ptr", &self.ptr)
343 .finish()
344 }
345}
346
347#[derive(PartialEq, Eq, Hash)]
349pub struct DispatchSemaphore {
350 ptr: *mut c_void,
351}
352
353unsafe impl Send for DispatchSemaphore {}
356unsafe impl Sync for DispatchSemaphore {}
357
358impl DispatchSemaphore {
359 #[must_use]
361 pub fn new(value: i64) -> Option<Self> {
362 if value < 0 {
363 return None;
364 }
365 let ptr = unsafe { crate::ffi::acf_dispatch_semaphore_holder_create(value) };
366 if ptr.is_null() {
367 None
368 } else {
369 Some(Self { ptr })
370 }
371 }
372
373 #[must_use]
375 pub fn signal(&self) -> i64 {
376 unsafe { crate::ffi::acf_dispatch_semaphore_holder_signal(self.ptr) }
377 }
378
379 #[must_use]
381 pub fn wait(&self, timeout: Option<Duration>) -> bool {
382 unsafe { crate::ffi::acf_dispatch_semaphore_holder_wait(self.ptr, timeout_ms(timeout)) }
383 }
384}
385
386crate::utils::retained::cf_retained!(
387 DispatchSemaphore,
388 field = ptr,
389 retain = crate::ffi::acf_object_retain,
390 release = crate::ffi::acf_object_release,
391 drop = unchecked,
392);
393
394impl fmt::Debug for DispatchSemaphore {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 f.debug_struct("DispatchSemaphore")
397 .field("ptr", &self.ptr)
398 .finish()
399 }
400}
401
402#[derive(PartialEq, Eq, Hash)]
404pub struct DispatchSource {
405 ptr: *mut c_void,
406}
407
408unsafe impl Send for DispatchSource {}
411unsafe impl Sync for DispatchSource {}
412
413impl DispatchSource {
414 #[must_use]
416 pub fn timer(interval: Duration, leeway: Duration) -> Self {
417 let interval_ns = u64::try_from(interval.as_nanos()).unwrap_or(u64::MAX);
418 let leeway_ns = u64::try_from(leeway.as_nanos()).unwrap_or(u64::MAX);
419 let ptr =
420 unsafe { crate::ffi::acf_dispatch_source_timer_create_ns(interval_ns, leeway_ns) };
421 assert!(!ptr.is_null(), "failed to create DispatchSource timer");
422 Self { ptr }
423 }
424
425 pub fn resume(&self) {
429 unsafe { crate::ffi::acf_dispatch_source_timer_resume(self.ptr) };
430 }
431
432 pub fn cancel(&self) {
437 unsafe { crate::ffi::acf_dispatch_source_timer_cancel(self.ptr) };
438 }
439
440 #[must_use]
442 pub fn fire_count(&self) -> u64 {
443 unsafe { crate::ffi::acf_dispatch_source_timer_fire_count(self.ptr) }
444 }
445}
446
447crate::utils::retained::cf_retained!(
448 DispatchSource,
449 field = ptr,
450 retain = crate::ffi::acf_object_retain,
451 release = crate::ffi::acf_object_release,
452 drop = unchecked,
453);
454
455impl fmt::Debug for DispatchSource {
456 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457 f.debug_struct("DispatchSource")
458 .field("ptr", &self.ptr)
459 .field("fire_count", &self.fire_count())
460 .finish()
461 }
462}