Skip to main content

compact_waitgroup/
group.rs

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