1#![cfg_attr(not(test), no_std)]
6#![cfg_attr(docsrs, feature(doc_cfg))]
7
8extern crate alloc;
9use alloc::boxed::Box;
10use alloc::vec::Vec;
11use core::future::Future;
12use core::pin::Pin;
13
14#[must_use = "Defer must be stored in a variable to execute the closure"]
16pub fn defer<F>(f: F) -> impl Drop
17where
18 F: FnOnce(),
19{
20 struct Defer<F: FnOnce()> {
21 f: Option<F>,
22 }
23
24 impl<F: FnOnce()> Drop for Defer<F> {
25 fn drop(&mut self) {
26 if let Some(f) = self.f.take() {
27 f();
28 }
29 }
30 }
31
32 Defer { f: Some(f) }
33}
34
35#[macro_export]
37macro_rules! defer {
38 ($e:expr) => {
39 let _guard = $crate::defer(|| $e);
40 let _ = &_guard;
41 };
42}
43
44pub struct AsyncScope {
46 defer: Vec<Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + 'static>> + 'static>>,
47}
48
49impl AsyncScope {
50 pub fn new() -> Self {
52 AsyncScope { defer: Vec::new() }
53 }
54
55 pub fn defer<F>(&mut self, f: F)
57 where
58 F: FnOnce() -> Pin<Box<dyn Future<Output = ()> + 'static>> + 'static,
59 {
60 self.defer.push(Box::new(move || Box::pin(f())));
61 }
62
63 pub async fn run(mut self) {
65 while let Some(f) = self.defer.pop() {
66 f().await;
67 }
68 }
69}
70
71#[macro_export]
73macro_rules! async_scope {
74 ($scope:ident, $body:block) => {
75 async {
76 let mut $scope = $crate::AsyncScope::new();
77 $body
78 $scope.run().await;
79 }
80 };
81}
82
83#[cfg_attr(docsrs, doc(cfg(feature = "no_alloc")))]
87#[cfg(any(feature = "no_alloc", test, docsrs))]
88pub mod no_alloc {
89 use alloc::boxed::Box;
90 use core::{future::Future, pin::Pin};
91
92 pub type DeferredFn = fn() -> Pin<Box<dyn Future<Output = ()> + 'static>>;
94
95 pub struct AsyncScopeNoAlloc<const N: usize> {
97 tasks: [Option<DeferredFn>; N],
98 len: usize,
99 }
100
101 impl<const N: usize> AsyncScopeNoAlloc<N> {
102 pub const fn new() -> Self {
104 Self {
105 tasks: [None; N],
106 len: 0,
107 }
108 }
109
110 pub fn defer(&mut self, f: DeferredFn) {
114 if self.len >= N {
115 panic!("No space left for more tasks.");
116 }
117 self.tasks[self.len] = Some(f);
118 self.len += 1;
119 }
120
121 pub async fn run(&mut self) {
123 while self.len > 0 {
124 self.len -= 1;
125 let task = self.tasks[self.len].take().unwrap();
126 (task)().await;
127 }
128 }
129 }
130
131 #[cfg_attr(docsrs, doc(cfg(feature = "no_alloc")))]
133 #[macro_export]
134 macro_rules! no_alloc_async_scope {
135 ($scope:ident : $cap:expr, $body:block) => {
136 async {
137 let mut $scope = $crate::no_alloc::AsyncScopeNoAlloc::<$cap>::new();
138 $body
139 $scope.run().await;
140 }
141 };
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 extern crate std;
148 use self::std::sync::{
149 Arc, Mutex,
150 atomic::{AtomicUsize, Ordering},
151 };
152 use super::*;
153
154 #[test]
155 fn test_sync_defer() {
156 println!("test_sync_defer start");
157 let val = Arc::new(AtomicUsize::new(0));
158 {
159 println!("in scope, val={}", val.load(Ordering::SeqCst));
160 let v = val.clone();
161 defer!(v.store(42, Ordering::SeqCst));
162 }
163 println!("out of scope, val={}", val.load(Ordering::SeqCst));
164 assert_eq!(val.load(Ordering::SeqCst), 42);
165 }
166
167 #[tokio::test]
168 async fn test_async_scope_order() {
169 println!("test_async_scope_order start");
170 let log = Arc::new(Mutex::new(Vec::new()));
171 {
172 let mut scope = AsyncScope::new();
173
174 let l1 = log.clone();
175 scope.defer(move || {
176 println!("push(1) scheduled");
177 let l1 = l1.clone();
178 Box::pin(async move {
179 println!("push(1) running");
180 l1.lock().unwrap().push(1);
181 })
182 });
183
184 let l2 = log.clone();
185 scope.defer(move || {
186 println!("push(2) scheduled");
187 let l2 = l2.clone();
188 Box::pin(async move {
189 println!("push(2) running");
190 l2.lock().unwrap().push(2);
191 })
192 });
193
194 scope.run().await;
195 }
196 let result = log.lock().unwrap().clone();
197 println!("final log: {:?}", result);
198 assert_eq!(result, vec![2, 1]);
199 }
200
201 #[tokio::test]
202 async fn test_async_scope_macro() {
203 println!("test_async_scope_macro start");
204 use crate::async_scope;
205 let flag = Arc::new(AtomicUsize::new(0));
206 {
207 let f = Arc::clone(&flag);
208 async_scope!(scope, {
209 let f2 = Arc::clone(&f);
210 scope.defer(move || {
211 println!("store(1) scheduled");
212 Box::pin(async move {
213 println!("store(1) running");
214 f2.store(1, Ordering::SeqCst);
215 })
216 });
217 println!("in scope, flag={}", f.load(Ordering::SeqCst));
218 })
219 .await;
220 }
221 println!("out of scope, flag={}", flag.load(Ordering::SeqCst));
222 assert_eq!(flag.load(Ordering::SeqCst), 1);
223 }
224
225 #[cfg(feature = "no_alloc")]
226 #[tokio::test]
227 async fn test_no_alloc_scope() {
228 println!("test_no_alloc_scope start");
229 use super::no_alloc::{AsyncScopeNoAlloc, DeferredFn};
230 use core::future::Future;
231 use core::pin::Pin;
232
233 fn task_one() -> Pin<Box<dyn Future<Output = ()> + 'static>> {
234 Box::pin(async {
235 println!("task_one running");
236 })
237 }
238 fn task_two() -> Pin<Box<dyn Future<Output = ()> + 'static>> {
239 Box::pin(async {
240 println!("task_two running");
241 })
242 }
243
244 let mut scope = AsyncScopeNoAlloc::<2>::new();
245 scope.defer(task_one as DeferredFn);
246 scope.defer(task_two as DeferredFn);
247 scope.run().await;
248 }
249}