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/// # Cancellation safety
52///
53/// This future is cancellation safe.
54///
55/// It is also safe to poll again after completion.
56///
57/// ```rust
58/// # use compact_waitgroup::MonoWaitGroup;
59/// # futures_executor::block_on(async {
60/// let (wg, token) = MonoWaitGroup::new();
61/// let mut wg = core::pin::pin!(wg);
62///
63/// assert!(!wg.is_done());
64///
65/// token.release();
66///
67/// wg.as_mut().await;
68/// assert!(wg.is_done());
69///
70/// // It is safe to await again (re-poll)
71/// wg.as_mut().await;
72/// assert!(wg.is_done());
73/// # });
74/// ```
75#[must_use]
76#[derive(Debug)]
77pub struct MonoWaitGroup(#[debug("done: {}", _0.is_done())] WaitGroupWrapper<TwinRef<MonoLayout>>);
78
79/// Clonable group token.
80#[must_use]
81#[derive(Clone, Debug)]
82pub struct GroupToken(
83 #[allow(unused)]
84 #[debug("done: {}", _0.is_done())]
85 ClonableTwinRef<SharedLayout>,
86);
87
88/// Non-clonable group token.
89#[must_use]
90#[derive(Debug)]
91pub struct MonoGroupToken(#[debug("done: {}", _0.is_done())] TwinRef<MonoLayout>);
92
93/// Factory of `GroupToken`.
94#[must_use]
95#[derive(Debug, Into)]
96pub struct GroupTokenFactory(GroupToken);
97
98impl WaitGroup {
99 /// Creates a new `WaitGroup` and a `GroupTokenFactory`.
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// use compact_waitgroup::WaitGroup;
105 ///
106 /// let (wg, factory) = WaitGroup::new();
107 /// // ... distribute token with factory ...
108 /// ```
109 pub fn new() -> (Self, GroupTokenFactory) {
110 let inner = SharedLayout::new();
111 let (wg, token) = TwinRef::new_clonable(inner);
112 (
113 Self(WaitGroupWrapper::new(wg)),
114 GroupTokenFactory(GroupToken(token)),
115 )
116 }
117
118 /// Checks if the `WaitGroup` has completed.
119 ///
120 /// This returns `true` if all `GroupToken`s have been dropped.
121 ///
122 /// # Examples
123 ///
124 /// ```
125 /// use compact_waitgroup::WaitGroup;
126 ///
127 /// let (wg, token) = WaitGroup::new();
128 /// assert!(!wg.is_done());
129 ///
130 /// drop(token);
131 /// assert!(wg.is_done());
132 /// ```
133 #[inline]
134 pub fn is_done(&self) -> bool {
135 self.0.is_done()
136 }
137}
138
139impl MonoWaitGroup {
140 /// Creates a new `MonoWaitGroup` and a single `MonoGroupToken`.
141 ///
142 /// This variant is optimized for scenarios where there is exactly one
143 /// worker task. The token cannot be cloned.
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use compact_waitgroup::MonoWaitGroup;
149 ///
150 /// let (wg, token) = MonoWaitGroup::new();
151 /// ```
152 pub fn new() -> (Self, MonoGroupToken) {
153 let inner = MonoLayout::new();
154 let (wg, token) = TwinRef::new_mono(inner);
155 (Self(WaitGroupWrapper::new(wg)), MonoGroupToken(token))
156 }
157
158 /// Checks if the `MonoWaitGroup` has completed.
159 ///
160 /// This returns `true` if the `MonoGroupToken` has been dropped.
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// use compact_waitgroup::MonoWaitGroup;
166 ///
167 /// let (wg, token) = MonoWaitGroup::new();
168 /// assert!(!wg.is_done());
169 ///
170 /// drop(token);
171 /// assert!(wg.is_done());
172 /// ```
173 #[inline]
174 pub fn is_done(&self) -> bool {
175 self.0.is_done()
176 }
177}
178
179impl Future for WaitGroup {
180 type Output = ();
181
182 #[inline]
183 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
184 Pin::new(&mut self.0).poll(cx)
185 }
186}
187
188impl Future for MonoWaitGroup {
189 type Output = ();
190
191 #[inline]
192 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
193 Pin::new(&mut self.0).poll(cx)
194 }
195}
196
197impl GroupTokenFactory {
198 /// Consumes the inner token.
199 ///
200 /// This is equivalent to dropping the factory.
201 #[inline]
202 pub fn release(self) {
203 drop(self);
204 }
205
206 #[inline]
207 pub fn into_token(self) -> GroupToken {
208 self.0
209 }
210
211 #[inline]
212 pub fn scope<T, F: FnOnce(GroupToken) -> T>(self, func: F) -> T {
213 func(self.into_token())
214 }
215}
216
217impl GroupToken {
218 /// Consumes the token.
219 ///
220 /// This is equivalent to dropping the token.
221 #[inline]
222 pub fn release(self) {
223 drop(self);
224 }
225}
226
227impl MonoGroupToken {
228 /// Consumes the token.
229 ///
230 /// This is equivalent to dropping the token.
231 #[inline]
232 pub fn release(self) {
233 drop(self);
234 }
235
236 #[inline]
237 pub fn into_token(self) -> Self {
238 self
239 }
240
241 #[inline]
242 pub fn scope<T, F: FnOnce(MonoGroupToken) -> T>(self, func: F) -> T {
243 func(self.into_token())
244 }
245}
246
247impl Drop for MonoGroupToken {
248 #[inline]
249 fn drop(&mut self) {
250 unsafe {
251 self.0.send_done();
252 }
253 }
254}