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}