Browse Source

feat: use_coroutine happily compiles

Jonathan Kelley 3 years ago
parent
commit
bd1535a0ed
2 changed files with 72 additions and 32 deletions
  1. 5 1
      packages/hooks/Cargo.toml
  2. 67 31
      packages/hooks/src/usecoroutine.rs

+ 5 - 1
packages/hooks/Cargo.toml

@@ -12,4 +12,8 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 
 [dependencies]
 [dependencies]
-dioxus-core = { path = "../../packages/core", version ="^0.1.7"}
+dioxus-core = { path = "../../packages/core", version = "^0.1.7" }
+
+
+[dev-dependencies]
+futures-util = { version = "0.3", default-features = false }

+ 67 - 31
packages/hooks/src/usecoroutine.rs

@@ -1,6 +1,7 @@
+#![warn(clippy::pedantic)]
+
 use bumpalo::boxed::Box as BumpBox;
 use bumpalo::boxed::Box as BumpBox;
 use dioxus_core::exports::bumpalo;
 use dioxus_core::exports::bumpalo;
-use dioxus_core::exports::futures_channel;
 use dioxus_core::{LazyNodes, ScopeState, TaskId};
 use dioxus_core::{LazyNodes, ScopeState, TaskId};
 use std::future::Future;
 use std::future::Future;
 use std::{cell::Cell, rc::Rc};
 use std::{cell::Cell, rc::Rc};
@@ -25,34 +26,35 @@ use std::{cell::Cell, rc::Rc};
 pub fn use_coroutine<'a, F>(
 pub fn use_coroutine<'a, F>(
     cx: &'a ScopeState,
     cx: &'a ScopeState,
     create_future: impl FnOnce() -> F + 'a,
     create_future: impl FnOnce() -> F + 'a,
-) -> UseCoroutine<F>
+) -> UseCoroutine<'a, F>
 where
 where
     F: Future<Output = ()> + 'static,
     F: Future<Output = ()> + 'static,
 {
 {
     let state = cx.use_hook(move |_| CoroutineInner {
     let state = cx.use_hook(move |_| CoroutineInner {
-        _id: None,
-        running: Default::default(),
+        task_id: Cell::new(None),
+        running: std::rc::Rc::default(),
+        run_count: Cell::new(0),
     });
     });
 
 
-    let mut bump = None;
-
     // as an optimization, we use the bump arena to allocate the callback instead of boxes
     // as an optimization, we use the bump arena to allocate the callback instead of boxes
     // that way we don't always call the constructor, but it's still efficient
     // that way we don't always call the constructor, but it's still efficient
-    cx.render(LazyNodes::new(move |f| {
-        bump.replace(f.bump());
-        f.static_text("")
-    }));
-
-    let mut slot = Some(create_future);
-
     // safety: bumpalo is limited in constructing unsized box types, so we have to do it through dynamic dispatch
     // safety: bumpalo is limited in constructing unsized box types, so we have to do it through dynamic dispatch
     let boxed: BumpBox<'a, dyn FnMut() -> F + 'a> = unsafe {
     let boxed: BumpBox<'a, dyn FnMut() -> F + 'a> = unsafe {
-        BumpBox::from_raw(bump.unwrap().alloc(move || {
-            let inner = slot.take().unwrap();
+        let mut bump = None;
+        cx.render(LazyNodes::new(move |f| {
+            bump.replace(f.bump());
+            f.static_text("")
+        }));
+        let mut slot = Some(create_future);
+        let bump = bump.expect("bump is assigned during render");
+        BumpBox::from_raw(bump.alloc(move || {
+            let inner = slot.take().expect("closure to not be called twice");
             inner()
             inner()
         }))
         }))
     };
     };
 
 
+    state.run_count.set(state.run_count.get() + 1);
+
     UseCoroutine {
     UseCoroutine {
         inner: state,
         inner: state,
         create_fut: Cell::new(Some(boxed)),
         create_fut: Cell::new(Some(boxed)),
@@ -62,7 +64,8 @@ where
 
 
 struct CoroutineInner {
 struct CoroutineInner {
     running: Rc<Cell<bool>>,
     running: Rc<Cell<bool>>,
-    _id: Option<TaskId>,
+    task_id: Cell<Option<TaskId>>,
+    run_count: Cell<u32>,
 }
 }
 
 
 pub struct UseCoroutine<'a, F: Future<Output = ()> + 'static> {
 pub struct UseCoroutine<'a, F: Future<Output = ()> + 'static> {
@@ -73,35 +76,50 @@ pub struct UseCoroutine<'a, F: Future<Output = ()> + 'static> {
 
 
 impl<'a, F: Future<Output = ()> + 'static> UseCoroutine<'a, F> {
 impl<'a, F: Future<Output = ()> + 'static> UseCoroutine<'a, F> {
     pub fn auto_start(&self, start: bool) -> &Self {
     pub fn auto_start(&self, start: bool) -> &Self {
-        todo!()
+        if start && self.inner.run_count.get() == 1 {
+            self.start();
+        }
+        self
+    }
+
+    pub fn is_running(&self) -> bool {
+        self.inner.running.get()
     }
     }
 
 
     pub fn start(&self) {
     pub fn start(&self) {
         if !self.is_running() {
         if !self.is_running() {
             if let Some(mut fut) = self.create_fut.take() {
             if let Some(mut fut) = self.create_fut.take() {
                 let fut = fut();
                 let fut = fut();
-                self.cx.push_future(fut);
-            }
-        }
-    }
+                let ready_handle = self.inner.running.clone();
 
 
-    pub fn is_running(&self) -> bool {
-        // self.inner.running.get()
-        false
-    }
+                let task = self.cx.push_future(async move {
+                    ready_handle.set(true);
+                    fut.await;
+                    ready_handle.set(false);
+                });
 
 
-    pub fn resume(&self) {
-        // self.cx.push_task(fut)
+                self.inner.task_id.set(Some(task));
+            }
+        }
     }
     }
 
 
-    pub fn stop(&self) {}
+    // todo: wire these up, either into the task system or into the coroutine system itself
+    // we would have change how we poll the coroutine and how its awaken
 
 
-    pub fn restart(&self) {}
+    // pub fn resume(&self) {}
+    // pub fn stop(&self) {}
+    // pub fn restart(&self) {}
 }
 }
 
 
-#[test]
-fn it_works() {
+#[cfg(test)]
+mod tests {
+    #![allow(unused)]
+
+    use super::*;
+    use dioxus_core::exports::futures_channel::mpsc::unbounded;
     use dioxus_core::prelude::*;
     use dioxus_core::prelude::*;
+    use futures_util::StreamExt;
+
     fn app(cx: Scope) -> Element {
     fn app(cx: Scope) -> Element {
         let poll_tasks = use_coroutine(&cx, || async {
         let poll_tasks = use_coroutine(&cx, || async {
             loop {
             loop {
@@ -113,4 +131,22 @@ fn it_works() {
 
 
         todo!()
         todo!()
     }
     }
+
+    fn app_with_channel(cx: Scope) -> Element {
+        let (tx, mut rx) = unbounded();
+
+        let tx = cx.use_hook(|_| tx);
+
+        let poll_tasks = use_coroutine(&cx, move || async move {
+            while let Some(msg) = rx.next().await {
+                println!("polling tasks: {}", msg);
+            }
+        });
+
+        poll_tasks.auto_start(true);
+
+        tx.unbounded_send("asd").unwrap();
+
+        todo!()
+    }
 }
 }