Skip to main content

dispatch2/
group.rs

1use alloc::boxed::Box;
2use core::ffi::c_void;
3
4use crate::generated::{dispatch_group_enter, dispatch_group_wait};
5use crate::{DispatchObject, DispatchQueue, DispatchRetained, DispatchTime};
6
7use super::utils::function_wrapper;
8use super::WaitError;
9
10dispatch_object!(
11    /// Dispatch group.
12    #[doc(alias = "dispatch_group_t")]
13    #[doc(alias = "dispatch_group_s")]
14    pub struct DispatchGroup;
15);
16
17dispatch_object_not_data!(unsafe DispatchGroup);
18
19impl DispatchGroup {
20    /// Submit a function to a [`DispatchQueue`] and associates it with the [`DispatchGroup`].
21    #[inline]
22    pub fn exec_async<F>(&self, queue: &DispatchQueue, work: F)
23    where
24        // We need `'static` to make sure any referenced values are borrowed for
25        // long enough since `work` will be performed asynchronously.
26        F: Send + FnOnce() + 'static,
27    {
28        let work_boxed = Box::into_raw(Box::new(work)).cast::<c_void>();
29
30        // Safety: All parameters cannot be null.
31        unsafe { Self::exec_async_f(self, queue, work_boxed, function_wrapper::<F>) };
32    }
33
34    /// Wait synchronously for the previously submitted functions to finish.
35    ///
36    /// # Errors
37    ///
38    /// Return [WaitError::Timeout] in case of timeout.
39    #[inline]
40    pub fn wait(&self, timeout: DispatchTime) -> Result<(), WaitError> {
41        let result = dispatch_group_wait(self, timeout);
42
43        match result {
44            0 => Ok(()),
45            _ => Err(WaitError::Timeout),
46        }
47    }
48
49    /// Schedule a function to be submitted to a [`DispatchQueue`] when a group of previously submitted functions have completed.
50    #[inline]
51    pub fn notify<F>(&self, queue: &DispatchQueue, work: F)
52    where
53        F: Send + FnOnce(),
54    {
55        let work_boxed = Box::into_raw(Box::new(work)).cast::<c_void>();
56
57        // Safety: All parameters cannot be null.
58        unsafe {
59            Self::notify_f(self, queue, work_boxed, function_wrapper::<F>);
60        }
61    }
62
63    /// Explicitly indicates that the function has entered the [`DispatchGroup`].
64    #[inline]
65    pub fn enter(&self) -> DispatchGroupGuard {
66        // SAFETY: TODO: Is it a soundness requirement that this is paired with leave?
67        unsafe { dispatch_group_enter(self) };
68
69        DispatchGroupGuard(self.retain())
70    }
71}
72
73/// Dispatch group guard.
74#[derive(Debug)]
75pub struct DispatchGroupGuard(DispatchRetained<DispatchGroup>);
76
77impl DispatchGroupGuard {
78    /// Explicitly indicate that the function in the [`DispatchGroup`] finished executing.
79    #[inline]
80    pub fn leave(self) {
81        // Drop.
82        let _ = self;
83    }
84}
85
86impl Drop for DispatchGroupGuard {
87    #[inline]
88    fn drop(&mut self) {
89        // SAFETY: TODO: Is it a soundness requirement that this is paired with enter?
90        unsafe { DispatchGroup::leave(&self.0) };
91    }
92}