Skip to content

Commit 5d79749

Browse files
authored
Merge pull request #162 from hyperware-ai/hf/hyperapp-add-spawn
hyperapp: add spawn
2 parents 9971afe + b9f1ead commit 5d79749

File tree

1 file changed

+34
-30
lines changed

1 file changed

+34
-30
lines changed

src/hyperapp.rs

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,10 @@ use serde::{Deserialize, Serialize};
1515
use thiserror::Error;
1616
use uuid::Uuid;
1717

18-
// macro_export puts it in the root,
19-
// so we re-export here so you can use as either
20-
// hyperware_process_lib::run_async
21-
// or
22-
// hyperware_process_lib::hyperapp::run_async
23-
pub use crate::run_async;
24-
2518
thread_local! {
19+
static SPAWN_QUEUE: RefCell<Vec<Pin<Box<dyn Future<Output = ()>>>>> = RefCell::new(Vec::new());
20+
21+
2622
pub static APP_CONTEXT: RefCell<AppContext> = RefCell::new(AppContext {
2723
hidden_state: None,
2824
executor: Executor::new(),
@@ -140,27 +136,46 @@ pub struct Executor {
140136
tasks: Vec<Pin<Box<dyn Future<Output = ()>>>>,
141137
}
142138

139+
pub fn spawn(fut: impl Future<Output = ()> + 'static) {
140+
SPAWN_QUEUE.with(|queue| {
141+
queue.borrow_mut().push(Box::pin(fut));
142+
})
143+
}
144+
143145
impl Executor {
144146
pub fn new() -> Self {
145147
Self { tasks: Vec::new() }
146148
}
147149

148-
pub fn spawn(&mut self, fut: impl Future<Output = ()> + 'static) {
149-
self.tasks.push(Box::pin(fut));
150-
}
151-
152150
pub fn poll_all_tasks(&mut self) {
153-
let mut ctx = Context::from_waker(noop_waker_ref());
154-
let mut completed = Vec::new();
151+
loop {
152+
// Drain any newly spawned tasks into our task list
153+
SPAWN_QUEUE.with(|queue| {
154+
self.tasks.append(&mut queue.borrow_mut());
155+
});
156+
157+
// Poll all tasks, collecting completed ones
158+
let mut completed = Vec::new();
159+
let mut ctx = Context::from_waker(noop_waker_ref());
160+
161+
for i in 0..self.tasks.len() {
162+
if let Poll::Ready(()) = self.tasks[i].as_mut().poll(&mut ctx) {
163+
completed.push(i);
164+
}
165+
}
155166

156-
for i in 0..self.tasks.len() {
157-
if let Poll::Ready(()) = self.tasks[i].as_mut().poll(&mut ctx) {
158-
completed.push(i);
167+
// Remove completed tasks immediately to prevent re-polling
168+
for idx in completed.into_iter().rev() {
169+
let _ = self.tasks.remove(idx);
159170
}
160-
}
161171

162-
for idx in completed.into_iter().rev() {
163-
let _ = self.tasks.remove(idx);
172+
// Check if there are new tasks spawned during polling
173+
let has_new_tasks = SPAWN_QUEUE.with(|queue| !queue.borrow().is_empty());
174+
175+
// Continue if new tasks were spawned, otherwise we're done
176+
if !has_new_tasks {
177+
break;
178+
}
164179
}
165180
}
166181
}
@@ -282,17 +297,6 @@ where
282297
return Err(AppSendError::SendError(e));
283298
}
284299

285-
#[macro_export]
286-
macro_rules! run_async {
287-
($($code:tt)*) => {
288-
hyperware_process_lib::hyperapp::APP_CONTEXT.with(|ctx| {
289-
ctx.borrow_mut().executor.spawn(async move {
290-
$($code)*
291-
})
292-
})
293-
};
294-
}
295-
296300
// Enum defining the state persistance behaviour
297301
#[derive(Clone)]
298302
pub enum SaveOptions {

0 commit comments

Comments
 (0)