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}