Skip to main content

compact_waitgroup/
group.rs

1use core::{
2    pin::Pin,
3    task::{Context, Poll},
4};
5
6use derive_more::{Debug, Into};
7
8use crate::{
9    layout::SharedLayout,
10    sync::{WaitGroupLayoutExt, WaitGroupWrapper},
11    twin_ref::{ClonableTwinRef, TwinRef},
12};
13
14#[cfg(feature = "compact-mono")]
15type MonoLayout = crate::layout::MonoLayout;
16#[cfg(not(feature = "compact-mono"))]
17type MonoLayout = crate::layout::SharedLayout;
18
19/// WaitGroup with clonable group tokens.
20///
21/// # Cancellation safety
22///
23/// This future is cancellation safe.
24///
25/// It is also safe to poll again after completion.
26///
27/// ```rust
28/// # use compact_waitgroup::WaitGroup;
29/// # futures_executor::block_on(async {
30/// let (wg, token) = WaitGroup::new();
31/// let mut wg = core::pin::pin!(wg);
32///
33/// assert!(!wg.is_done());
34///
35/// token.release();
36///
37/// wg.as_mut().await;
38/// assert!(wg.is_done());
39///
40/// // It is safe to await again (re-poll)
41/// wg.as_mut().await;
42/// assert!(wg.is_done());
43/// # });
44/// ```
45#[must_use]
46#[derive(Debug)]
47pub struct WaitGroup(#[debug("done: {}", _0.is_done())] WaitGroupWrapper<TwinRef<SharedLayout>>);
48
49/// WaitGroup with a single non-clonable group token.
50///
51/// This variant is optimized for scenarios where there is exactly one worker
52/// task, and the group token cannot be cloned.
53///
54/// # Cancellation safety
55///
56/// This future is cancellation safe.
57///
58/// It is also safe to poll again after completion.
59///
60/// ```rust
61/// # use compact_waitgroup::MonoWaitGroup;
62/// # futures_executor::block_on(async {
63/// let (wg, token) = MonoWaitGroup::new();
64/// let mut wg = core::pin::pin!(wg);
65///
66/// assert!(!wg.is_done());
67///
68/// token.release();
69///
70/// wg.as_mut().await;
71/// assert!(wg.is_done());
72///
73/// // It is safe to await again (re-poll)
74/// wg.as_mut().await;
75/// assert!(wg.is_done());
76/// # });
77/// ```
78#[must_use]
79#[derive(Debug)]
80pub struct MonoWaitGroup(#[debug("done: {}", _0.is_done())] WaitGroupWrapper<TwinRef<MonoLayout>>);
81
82/// Clonable group token.
83///
84/// Used by [`WaitGroup`] to signal task completion. Can be cloned and
85/// distributed among multiple worker tasks. Dropping or releasing all tokens
86/// completes the associated [`WaitGroup`].
87#[must_use]
88#[derive(Clone, Debug)]
89pub struct GroupToken(
90    #[allow(unused)]
91    #[debug("done: {}", _0.is_done())]
92    ClonableTwinRef<SharedLayout>,
93);
94
95/// Non-clonable group token.
96///
97/// Used by [`MonoWaitGroup`] for a single worker task. Dropping or releasing
98/// this token completes the associated [`MonoWaitGroup`].
99#[must_use]
100#[derive(Debug)]
101pub struct MonoGroupToken(#[debug("done: {}", _0.is_done())] TwinRef<MonoLayout>);
102
103/// Factory of [`GroupToken`].
104///
105/// Provides methods to obtain or scope the clonable token for distribution.
106#[must_use]
107#[derive(Debug, Into)]
108pub struct GroupTokenFactory(GroupToken);
109
110impl WaitGroup {
111    /// Creates a new `WaitGroup` and a [`GroupTokenFactory`].
112    pub fn new() -> (Self, GroupTokenFactory) {
113        let inner = SharedLayout::new();
114        let (wg, token) = TwinRef::new_clonable(inner);
115        (
116            Self(WaitGroupWrapper::new(wg)),
117            GroupTokenFactory(GroupToken(token)),
118        )
119    }
120
121    /// Checks if the `WaitGroup` has completed.
122    ///
123    /// This returns `true` if all [`GroupToken`]s have been dropped.
124    #[inline]
125    pub fn is_done(&self) -> bool {
126        self.0.is_done()
127    }
128}
129
130impl MonoWaitGroup {
131    /// Creates a new `MonoWaitGroup` and a single [`MonoGroupToken`].
132    pub fn new() -> (Self, MonoGroupToken) {
133        let inner = MonoLayout::new();
134        let (wg, token) = TwinRef::new_mono(inner);
135        (Self(WaitGroupWrapper::new(wg)), MonoGroupToken(token))
136    }
137
138    /// Checks if the `MonoWaitGroup` has completed.
139    ///
140    /// This returns `true` if the [`MonoGroupToken`] has been dropped.
141    #[inline]
142    pub fn is_done(&self) -> bool {
143        self.0.is_done()
144    }
145}
146
147impl Future for WaitGroup {
148    type Output = ();
149
150    #[inline]
151    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
152        Pin::new(&mut self.0).poll(cx)
153    }
154}
155
156impl Future for MonoWaitGroup {
157    type Output = ();
158
159    #[inline]
160    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
161        Pin::new(&mut self.0).poll(cx)
162    }
163}
164
165impl GroupTokenFactory {
166    /// Consumes the inner token.
167    ///
168    /// This is equivalent to dropping the factory.
169    #[inline]
170    pub fn release(self) {
171        drop(self);
172    }
173
174    /// Extracts the inner [`GroupToken`].
175    #[inline]
176    pub fn into_token(self) -> GroupToken {
177        self.0
178    }
179
180    /// Executes a closure with the inner [`GroupToken`].
181    #[inline]
182    pub fn scope<T, F: FnOnce(GroupToken) -> T>(self, func: F) -> T {
183        func(self.into_token())
184    }
185}
186
187impl GroupToken {
188    /// Consumes the token.
189    ///
190    /// This is equivalent to dropping the token.
191    #[inline]
192    pub fn release(self) {
193        drop(self);
194    }
195}
196
197impl MonoGroupToken {
198    /// Consumes the token.
199    ///
200    /// This is equivalent to dropping the token.
201    #[inline]
202    pub fn release(self) {
203        drop(self);
204    }
205
206    /// Returns the token itself.
207    ///
208    /// Provided for API consistency with [`GroupTokenFactory`].
209    #[inline]
210    pub fn into_token(self) -> Self {
211        self
212    }
213
214    /// Executes a closure with the token itself.
215    ///
216    /// Provided for API consistency with [`GroupTokenFactory`].
217    #[inline]
218    pub fn scope<T, F: FnOnce(MonoGroupToken) -> T>(self, func: F) -> T {
219        func(self.into_token())
220    }
221}
222
223impl Drop for MonoGroupToken {
224    #[inline]
225    fn drop(&mut self) {
226        unsafe {
227            self.0.send_done();
228        }
229    }
230}