apple_cf/
dispatch_queue.rs1#![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 {}
81unsafe impl Sync for DispatchQueue {}
82
83impl DispatchQueue {
84 #[must_use]
104 pub fn new(label: &str, qos: DispatchQoS) -> Self {
105 let c_label = CString::new(label).expect("Label contains null byte");
106 let ptr = unsafe { crate::ffi::dispatch_queue_create(c_label.as_ptr(), qos as i32) };
107 assert!(!ptr.is_null(), "Failed to create dispatch queue");
108 Self { ptr }
109 }
110
111 #[must_use]
115 pub const fn as_ptr(&self) -> *const c_void {
116 self.ptr
117 }
118
119 #[must_use]
120 const fn as_mut_ptr(&self) -> *mut c_void {
121 self.ptr.cast_mut()
122 }
123}
124
125impl Clone for DispatchQueue {
126 fn clone(&self) -> Self {
127 unsafe {
128 Self {
129 ptr: crate::ffi::dispatch_queue_retain(self.ptr),
130 }
131 }
132 }
133}
134
135impl Drop for DispatchQueue {
136 fn drop(&mut self) {
137 unsafe {
138 crate::ffi::dispatch_queue_release(self.ptr);
139 }
140 }
141}
142
143impl fmt::Debug for DispatchQueue {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 f.debug_struct("DispatchQueue")
146 .field("ptr", &self.ptr)
147 .finish()
148 }
149}
150
151impl fmt::Display for DispatchQueue {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(f, "DispatchQueue")
154 }
155}
156
157fn timeout_ms(timeout: Option<Duration>) -> i64 {
158 timeout.map_or(-1, |duration| {
159 i64::try_from(duration.as_millis()).unwrap_or(i64::MAX)
160 })
161}
162
163struct DispatchOnceTask {
164 site: &'static str,
165 work: Option<Box<dyn FnOnce() + Send + 'static>>,
166}
167
168struct DispatchApplyTask {
169 work: Box<dyn Fn(usize) + Send + Sync + 'static>,
170}
171
172extern "C" fn dispatch_once_trampoline(context: *mut c_void) {
173 if context.is_null() {
174 return;
175 }
176 let mut task = unsafe { Box::from_raw(context.cast::<DispatchOnceTask>()) };
177 if let Some(work) = task.work.take() {
178 panic_safe::catch_user_panic(task.site, work);
179 }
180}
181
182extern "C" fn dispatch_apply_trampoline(iteration: usize, context: *mut c_void) {
183 if context.is_null() {
184 return;
185 }
186 let task = unsafe { &*context.cast::<DispatchApplyTask>() };
187 panic_safe::catch_user_panic("dispatch_apply", || (task.work)(iteration));
188}
189
190pub fn dispatch_async<F>(queue: &DispatchQueue, work: F)
192where
193 F: FnOnce() + Send + 'static,
194{
195 let task = Box::new(DispatchOnceTask {
196 site: "dispatch_async",
197 work: Some(Box::new(work)),
198 });
199 unsafe {
200 crate::ffi::acf_dispatch_async_f(
201 queue.as_mut_ptr(),
202 Box::into_raw(task).cast(),
203 dispatch_once_trampoline,
204 );
205 }
206}
207
208pub fn dispatch_async_and_wait<F>(queue: &DispatchQueue, work: F)
210where
211 F: FnOnce() + Send + 'static,
212{
213 let task = Box::new(DispatchOnceTask {
214 site: "dispatch_async_and_wait",
215 work: Some(Box::new(work)),
216 });
217 unsafe {
218 crate::ffi::acf_dispatch_async_and_wait_f(
219 queue.as_mut_ptr(),
220 Box::into_raw(task).cast(),
221 dispatch_once_trampoline,
222 );
223 }
224}
225
226pub fn dispatch_apply<F>(iterations: usize, queue: &DispatchQueue, work: F)
228where
229 F: Fn(usize) + Send + Sync + 'static,
230{
231 if iterations == 0 {
232 return;
233 }
234 let task = Box::new(DispatchApplyTask {
235 work: Box::new(work),
236 });
237 let raw = Box::into_raw(task);
238 unsafe {
239 crate::ffi::acf_dispatch_apply_f(
240 iterations,
241 queue.as_mut_ptr(),
242 raw.cast(),
243 dispatch_apply_trampoline,
244 );
245 drop(Box::from_raw(raw));
246 }
247}
248
249#[derive(PartialEq, Eq, Hash)]
251pub struct DispatchGroup {
252 ptr: *mut c_void,
253}
254
255unsafe impl Send for DispatchGroup {}
256unsafe impl Sync for DispatchGroup {}
257
258impl DispatchGroup {
259 #[must_use]
261 pub fn new() -> Self {
262 let ptr = unsafe { crate::ffi::acf_dispatch_group_create() };
263 assert!(!ptr.is_null(), "failed to create DispatchGroup");
264 Self { ptr }
265 }
266
267 pub fn enter(&self) {
269 unsafe { crate::ffi::acf_dispatch_group_enter(self.ptr) };
270 }
271
272 pub fn leave(&self) {
274 unsafe { crate::ffi::acf_dispatch_group_leave(self.ptr) };
275 }
276
277 #[must_use]
279 pub fn wait(&self, timeout: Option<Duration>) -> bool {
280 unsafe { crate::ffi::acf_dispatch_group_wait(self.ptr, timeout_ms(timeout)) }
281 }
282}
283
284impl Default for DispatchGroup {
285 fn default() -> Self {
286 Self::new()
287 }
288}
289
290impl Clone for DispatchGroup {
291 fn clone(&self) -> Self {
292 Self {
293 ptr: unsafe { crate::ffi::acf_object_retain(self.ptr) },
294 }
295 }
296}
297
298impl Drop for DispatchGroup {
299 fn drop(&mut self) {
300 unsafe { crate::ffi::acf_object_release(self.ptr) };
301 }
302}
303
304impl fmt::Debug for DispatchGroup {
305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306 f.debug_struct("DispatchGroup")
307 .field("ptr", &self.ptr)
308 .finish()
309 }
310}
311
312#[derive(PartialEq, Eq, Hash)]
314pub struct DispatchSemaphore {
315 ptr: *mut c_void,
316}
317
318unsafe impl Send for DispatchSemaphore {}
319unsafe impl Sync for DispatchSemaphore {}
320
321impl DispatchSemaphore {
322 #[must_use]
324 pub fn new(value: i64) -> Self {
325 let ptr = unsafe { crate::ffi::acf_dispatch_semaphore_create(value) };
326 assert!(!ptr.is_null(), "failed to create DispatchSemaphore");
327 Self { ptr }
328 }
329
330 #[must_use]
332 pub fn signal(&self) -> i64 {
333 unsafe { crate::ffi::acf_dispatch_semaphore_signal(self.ptr) }
334 }
335
336 #[must_use]
338 pub fn wait(&self, timeout: Option<Duration>) -> bool {
339 unsafe { crate::ffi::acf_dispatch_semaphore_wait(self.ptr, timeout_ms(timeout)) }
340 }
341}
342
343impl Clone for DispatchSemaphore {
344 fn clone(&self) -> Self {
345 Self {
346 ptr: unsafe { crate::ffi::acf_object_retain(self.ptr) },
347 }
348 }
349}
350
351impl Drop for DispatchSemaphore {
352 fn drop(&mut self) {
353 unsafe { crate::ffi::acf_object_release(self.ptr) };
354 }
355}
356
357impl fmt::Debug for DispatchSemaphore {
358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359 f.debug_struct("DispatchSemaphore")
360 .field("ptr", &self.ptr)
361 .finish()
362 }
363}
364
365#[derive(PartialEq, Eq, Hash)]
367pub struct DispatchSource {
368 ptr: *mut c_void,
369}
370
371unsafe impl Send for DispatchSource {}
372unsafe impl Sync for DispatchSource {}
373
374impl DispatchSource {
375 #[must_use]
377 pub fn timer(interval: Duration, leeway: Duration) -> Self {
378 let interval_ms = u64::try_from(interval.as_millis()).unwrap_or(u64::MAX);
379 let leeway_ms = u64::try_from(leeway.as_millis()).unwrap_or(u64::MAX);
380 let ptr = unsafe { crate::ffi::acf_dispatch_source_timer_create(interval_ms, leeway_ms) };
381 assert!(!ptr.is_null(), "failed to create DispatchSource timer");
382 Self { ptr }
383 }
384
385 pub fn resume(&self) {
387 unsafe { crate::ffi::acf_dispatch_source_timer_resume(self.ptr) };
388 }
389
390 pub fn cancel(&self) {
392 unsafe { crate::ffi::acf_dispatch_source_timer_cancel(self.ptr) };
393 }
394
395 #[must_use]
397 pub fn fire_count(&self) -> u64 {
398 unsafe { crate::ffi::acf_dispatch_source_timer_fire_count(self.ptr) }
399 }
400}
401
402impl Clone for DispatchSource {
403 fn clone(&self) -> Self {
404 Self {
405 ptr: unsafe { crate::ffi::acf_object_retain(self.ptr) },
406 }
407 }
408}
409
410impl Drop for DispatchSource {
411 fn drop(&mut self) {
412 unsafe { crate::ffi::acf_object_release(self.ptr) };
413 }
414}
415
416impl fmt::Debug for DispatchSource {
417 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 f.debug_struct("DispatchSource")
419 .field("ptr", &self.ptr)
420 .field("fire_count", &self.fire_count())
421 .finish()
422 }
423}