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