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