#![allow(clippy::missing_panics_doc)]
use crate::utils::panic_safe;
use std::ffi::{c_void, CString};
use std::fmt;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum DispatchQoS {
Background = 0,
Utility = 1,
#[default]
Default = 2,
UserInitiated = 3,
UserInteractive = 4,
}
pub struct DispatchQueue {
ptr: *const c_void,
}
unsafe impl Send for DispatchQueue {}
unsafe impl Sync for DispatchQueue {}
impl DispatchQueue {
#[must_use]
pub fn new(label: &str, qos: DispatchQoS) -> Self {
let c_label = CString::new(label).expect("Label contains null byte");
let ptr = unsafe { crate::ffi::acf_dispatch_queue_create(c_label.as_ptr(), qos as i32) };
assert!(!ptr.is_null(), "Failed to create dispatch queue");
Self { ptr }
}
#[must_use]
pub const fn as_ptr(&self) -> *const c_void {
self.ptr
}
#[must_use]
const fn as_mut_ptr(&self) -> *mut c_void {
self.ptr.cast_mut()
}
}
impl Clone for DispatchQueue {
fn clone(&self) -> Self {
unsafe {
Self {
ptr: crate::ffi::dispatch_queue_retain(self.ptr),
}
}
}
}
impl Drop for DispatchQueue {
fn drop(&mut self) {
unsafe {
crate::ffi::dispatch_queue_release(self.ptr);
}
}
}
impl fmt::Debug for DispatchQueue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DispatchQueue")
.field("ptr", &self.ptr)
.finish()
}
}
impl fmt::Display for DispatchQueue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DispatchQueue")
}
}
fn timeout_ms(timeout: Option<Duration>) -> i64 {
timeout.map_or(-1, |duration| {
i64::try_from(duration.as_millis()).unwrap_or(i64::MAX)
})
}
struct DispatchOnceTask {
site: &'static str,
work: Option<Box<dyn FnOnce() + Send + 'static>>,
}
struct DispatchApplyTask {
work: Box<dyn Fn(usize) + Send + Sync + 'static>,
}
extern "C" fn dispatch_once_trampoline(context: *mut c_void) {
if context.is_null() {
return;
}
let mut task = unsafe { Box::from_raw(context.cast::<DispatchOnceTask>()) };
if let Some(work) = task.work.take() {
panic_safe::catch_user_panic(task.site, work);
}
}
extern "C" fn dispatch_apply_trampoline(iteration: usize, context: *mut c_void) {
if context.is_null() {
return;
}
let task = unsafe { &*context.cast::<DispatchApplyTask>() };
panic_safe::catch_user_panic("dispatch_apply", || (task.work)(iteration));
}
pub fn dispatch_async<F>(queue: &DispatchQueue, work: F)
where
F: FnOnce() + Send + 'static,
{
let task = Box::new(DispatchOnceTask {
site: "dispatch_async",
work: Some(Box::new(work)),
});
unsafe {
crate::ffi::acf_dispatch_async_f(
queue.as_mut_ptr(),
Box::into_raw(task).cast(),
dispatch_once_trampoline,
);
}
}
pub fn dispatch_async_and_wait<F>(queue: &DispatchQueue, work: F)
where
F: FnOnce() + Send + 'static,
{
let task = Box::new(DispatchOnceTask {
site: "dispatch_async_and_wait",
work: Some(Box::new(work)),
});
unsafe {
crate::ffi::acf_dispatch_async_and_wait_f(
queue.as_mut_ptr(),
Box::into_raw(task).cast(),
dispatch_once_trampoline,
);
}
}
pub fn dispatch_apply<F>(iterations: usize, queue: &DispatchQueue, work: F)
where
F: Fn(usize) + Send + Sync + 'static,
{
if iterations == 0 {
return;
}
let task = Box::new(DispatchApplyTask {
work: Box::new(work),
});
let raw = Box::into_raw(task);
unsafe {
crate::ffi::acf_dispatch_apply_f(
iterations,
queue.as_mut_ptr(),
raw.cast(),
dispatch_apply_trampoline,
);
drop(Box::from_raw(raw));
}
}
#[derive(PartialEq, Eq, Hash)]
pub struct DispatchGroup {
ptr: *mut c_void,
}
unsafe impl Send for DispatchGroup {}
unsafe impl Sync for DispatchGroup {}
impl DispatchGroup {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { crate::ffi::acf_dispatch_group_create() };
assert!(!ptr.is_null(), "failed to create DispatchGroup");
Self { ptr }
}
pub fn enter(&self) {
unsafe { crate::ffi::acf_dispatch_group_enter(self.ptr) };
}
pub fn leave(&self) {
unsafe { crate::ffi::acf_dispatch_group_leave(self.ptr) };
}
#[must_use]
pub fn wait(&self, timeout: Option<Duration>) -> bool {
unsafe { crate::ffi::acf_dispatch_group_wait(self.ptr, timeout_ms(timeout)) }
}
}
impl Default for DispatchGroup {
fn default() -> Self {
Self::new()
}
}
impl Clone for DispatchGroup {
fn clone(&self) -> Self {
Self {
ptr: unsafe { crate::ffi::acf_object_retain(self.ptr) },
}
}
}
impl Drop for DispatchGroup {
fn drop(&mut self) {
unsafe { crate::ffi::acf_object_release(self.ptr) };
}
}
impl fmt::Debug for DispatchGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DispatchGroup")
.field("ptr", &self.ptr)
.finish()
}
}
#[derive(PartialEq, Eq, Hash)]
pub struct DispatchSemaphore {
ptr: *mut c_void,
}
unsafe impl Send for DispatchSemaphore {}
unsafe impl Sync for DispatchSemaphore {}
impl DispatchSemaphore {
#[must_use]
pub fn new(value: i64) -> Self {
let ptr = unsafe { crate::ffi::acf_dispatch_semaphore_create(value) };
assert!(!ptr.is_null(), "failed to create DispatchSemaphore");
Self { ptr }
}
#[must_use]
pub fn signal(&self) -> i64 {
unsafe { crate::ffi::acf_dispatch_semaphore_signal(self.ptr) }
}
#[must_use]
pub fn wait(&self, timeout: Option<Duration>) -> bool {
unsafe { crate::ffi::acf_dispatch_semaphore_wait(self.ptr, timeout_ms(timeout)) }
}
}
impl Clone for DispatchSemaphore {
fn clone(&self) -> Self {
Self {
ptr: unsafe { crate::ffi::acf_object_retain(self.ptr) },
}
}
}
impl Drop for DispatchSemaphore {
fn drop(&mut self) {
unsafe { crate::ffi::acf_object_release(self.ptr) };
}
}
impl fmt::Debug for DispatchSemaphore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DispatchSemaphore")
.field("ptr", &self.ptr)
.finish()
}
}
#[derive(PartialEq, Eq, Hash)]
pub struct DispatchSource {
ptr: *mut c_void,
}
unsafe impl Send for DispatchSource {}
unsafe impl Sync for DispatchSource {}
impl DispatchSource {
#[must_use]
pub fn timer(interval: Duration, leeway: Duration) -> Self {
let interval_ms = u64::try_from(interval.as_millis()).unwrap_or(u64::MAX);
let leeway_ms = u64::try_from(leeway.as_millis()).unwrap_or(u64::MAX);
let ptr = unsafe { crate::ffi::acf_dispatch_source_timer_create(interval_ms, leeway_ms) };
assert!(!ptr.is_null(), "failed to create DispatchSource timer");
Self { ptr }
}
pub fn resume(&self) {
unsafe { crate::ffi::acf_dispatch_source_timer_resume(self.ptr) };
}
pub fn cancel(&self) {
unsafe { crate::ffi::acf_dispatch_source_timer_cancel(self.ptr) };
}
#[must_use]
pub fn fire_count(&self) -> u64 {
unsafe { crate::ffi::acf_dispatch_source_timer_fire_count(self.ptr) }
}
}
impl Clone for DispatchSource {
fn clone(&self) -> Self {
Self {
ptr: unsafe { crate::ffi::acf_object_retain(self.ptr) },
}
}
}
impl Drop for DispatchSource {
fn drop(&mut self) {
unsafe { crate::ffi::acf_object_release(self.ptr) };
}
}
impl fmt::Debug for DispatchSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DispatchSource")
.field("ptr", &self.ptr)
.field("fire_count", &self.fire_count())
.finish()
}
}