From 23727addf470e271ffb2866f0d9718ce256460d1 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Mon, 12 Jan 2026 18:01:00 +0200 Subject: [PATCH 1/3] mvp scope implementation --- src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++++ tests/scope.rs | 19 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/scope.rs diff --git a/src/lib.rs b/src/lib.rs index b65c33c..a7e2f81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -389,6 +389,44 @@ impl<'a> Executor<'a> { // and will never be moved until it's dropped. Pin::new(unsafe { &*ptr }) } + + pub fn scope<'e, R>( + &'e self, + callback: impl Fn(&mut Scope<'e>) -> R, + ) -> impl Future { + let mut scope = Scope::new(self.state().get_ref()); + let res = callback(&mut scope); + let joined = async move { + let tasks = scope.tasks; + for task in tasks { + task.await; + } + res + }; + joined + } +} + +pub struct Scope<'e> { + state: Pin<&'e State>, + tasks: Vec>, +} + +impl<'e> Scope<'e> { + fn new(state: &'e State) -> Self { + Scope { + state: Pin::new(state), + tasks: Vec::new(), + } + } + + pub fn spawn(&mut self, future: F) + where + F: Future + Send + 'e, + { + let task = unsafe { Executor::spawn_inner(self.state, future, &mut self.state.active()) }; + self.tasks.push(task); + } } impl Drop for Executor<'_> { diff --git a/tests/scope.rs b/tests/scope.rs new file mode 100644 index 0000000..ad6d7f1 --- /dev/null +++ b/tests/scope.rs @@ -0,0 +1,19 @@ +use async_executor::Executor; + +#[test] +fn test_basic_scope() { + let ex = Executor::new(); + let data = std::sync::Mutex::new(0i32); + futures_lite::future::block_on(ex.run(async { + ex.scope(|scope| { + scope.spawn(async { + *data.lock().unwrap() += 1; + }); + scope.spawn(async { + *data.lock().unwrap() += 4; + }); + }) + .await; + })); + assert_eq!(*data.lock().unwrap(), 5); +} From 130be4eb23310886e820d41d04213fdae3e5be8e Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Mon, 12 Jan 2026 18:40:09 +0200 Subject: [PATCH 2/3] allow any type as result of Scope::spawn --- src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a7e2f81..d7cda76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -420,10 +420,13 @@ impl<'e> Scope<'e> { } } - pub fn spawn(&mut self, future: F) + pub fn spawn(&mut self, future: F) where - F: Future + Send + 'e, + F: Future + Send + 'e, { + let future = async { + _ = future.await; + }; let task = unsafe { Executor::spawn_inner(self.state, future, &mut self.state.active()) }; self.tasks.push(task); } From 4fe7666697771ec34ae547b76fb3dd7e0c4eff05 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Mon, 12 Jan 2026 18:46:48 +0200 Subject: [PATCH 3/3] change scope closure to take FnOnce --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d7cda76..6a611fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -392,7 +392,7 @@ impl<'a> Executor<'a> { pub fn scope<'e, R>( &'e self, - callback: impl Fn(&mut Scope<'e>) -> R, + callback: impl FnOnce(&mut Scope<'e>) -> R, ) -> impl Future { let mut scope = Scope::new(self.state().get_ref()); let res = callback(&mut scope);