1
0
Эх сурвалжийг харах

wip: changes to scheduler

Jonathan Kelley 3 жил өмнө
parent
commit
098d3821ed

+ 1 - 1
.vscode/settings.json

@@ -1,4 +1,4 @@
 {
-  "rust-analyzer.inlayHints.enable": false,
+  "rust-analyzer.inlayHints.enable": true,
   "rust-analyzer.cargo.allFeatures": true
 }

+ 0 - 1
packages/core-macro/examples/fc.rs

@@ -1 +0,0 @@
-fn main() {}

+ 1 - 1
packages/core-macro/examples/prop_test.rs

@@ -20,5 +20,5 @@ struct SomeProps {
 /// This implementation does not require a "PartialEq" because it does not memoize
 #[derive(dioxus_core_macro::Props)]
 struct SomePropsTwo<'a> {
-    a: &'a str,
+    _a: &'a str,
 }

+ 0 - 125
packages/core-macro/examples/rsxt.rs

@@ -1,125 +0,0 @@
-pub mod dioxus {
-    pub mod builder {
-        pub struct Builder;
-
-        struct AttrVal;
-
-        impl Into<AttrVal> for &'static str {
-            fn into(self) -> AttrVal {
-                todo!()
-            }
-        }
-
-        impl Into<AttrVal> for String {
-            fn into(self) -> AttrVal {
-                todo!()
-            }
-        }
-        // impl<T> From<T> for AttrVal {
-        //     fn from(_: T) -> Self {
-        //         todo!()
-        //     }
-        // }
-
-        impl Builder {
-            // fn attr<T>(mut self, key: &str, value: impl Into<AttrVal>) -> Self {
-            pub fn attr<T>(self, _key: &str, _value: T) -> Self {
-                Self
-            }
-
-            pub fn on<T>(self, _key: &str, _value: T) -> Self {
-                Self
-            }
-
-            pub fn finish(self) {
-                // Self
-            }
-        }
-
-        pub struct Bump;
-        pub fn div(_bump: &Bump) -> Builder {
-            todo!()
-        }
-        pub fn h1(_bump: &Bump) -> Builder {
-            todo!()
-        }
-        pub fn h2(_bump: &Bump) -> Builder {
-            todo!()
-        }
-    }
-}
-
-pub fn main() {
-    // render(rsx! {
-    //     div { // we can actually support just a list of nodes too
-    //         h1 {"Hello Dioxus"}
-    //         p {"This is a beautful app you're building"}
-    //         section {
-    //             "custom section to the rescue",
-    //             class: "abc123"
-    //         }
-    //         span {
-    //             class: "abc123"
-    //             "Try backwards too."
-    //             "Anything goes!"
-    //             "As long as it's within the rules"
-    //             {0..10.map(|f| rsx!{
-    //                 div {
-    //                     h3 {"totally okay to drop in iterators and expressions"}
-    //                     p {"however, debug information is lost"}
-    //                 }
-    //             })}
-    //         }
-    //         span {
-    //             "Feel free"
-    //             class: "abc123"
-    //             "To mix to your heart's content"
-    //         }
-    //         span { class: "some-very-long-and-tedious-class-name-is-now-separated"
-    //             "Very ergonomic"
-    //         }
-    //         span { "Innovative design 🛠"
-    //             class: "some-very-long-and-tedious-class-name-is-now-separated"
-    //         }
-    //     }
-    // });
-
-    let _g = String::from("asd");
-
-    // let lazy = rsx! {
-    //     div {
-    //         a: "asd",
-    //         a: "asd",
-    //         a: "asd",
-    //         a: "asd",
-    //         a: "asd",
-    //         // a: {rsx!{ h1 {"hello world"} }}, // include
-    //         a: {&g},
-    //         b: {1 + 2},
-    //         onclick: {move |e: ()| {
-    //             println!("hello world!")
-    //         }},
-    //         div {
-    //             a: "asd"
-    //             div {
-    //                 div {
-    //                     div {
-
-    //                     }
-    //                 }
-    //             }
-    //         }
-    //         h1 {
-
-    //         }
-    //         h2 {
-    //             "child"
-    //         }
-    //         "Childnode"
-    //     }
-    // };
-
-    // render(lazy);
-}
-
-fn render(_f: impl Fn(&dioxus::builder::Bump)) {}

+ 1 - 1
packages/core/benches/jsframework.rs

@@ -22,7 +22,7 @@ criterion_group!(mbenches, create_rows);
 criterion_main!(mbenches);
 
 fn create_rows(c: &mut Criterion) {
-    static App: FC<()> = |cx, props| {
+    static App: FC<()> = |cx, _| {
         let mut rng = SmallRng::from_entropy();
         let rows = (0..10_000).map(|f| {
             let label = Label::new(&mut rng);

+ 6 - 2
packages/core/examples/alternative.rs

@@ -1,8 +1,12 @@
 use dioxus_core::prelude::*;
 
-fn main() {}
+fn main() {
+    let mut dom = VirtualDom::new(EXAMPLE);
+    dom.rebuild();
+    println!("{}", dom);
+}
 
-pub static Example: FC<()> = |cx, props| {
+pub static EXAMPLE: FC<()> = |cx, _| {
     let list = (0..10).map(|_f| LazyNodes::new(move |_f| todo!()));
 
     cx.render(LazyNodes::new(move |cx| {

+ 7 - 31
packages/core/examples/async.rs

@@ -1,39 +1,15 @@
 use dioxus_core::prelude::*;
 
-fn main() {}
+fn main() {
+    let mut dom = VirtualDom::new(App);
+    dom.rebuild();
+}
 
 const App: FC<()> = |cx, props| {
-    // create a new future
-    let _fut = cx.use_hook(
-        |_| {
-            //
-            async { loop {} }
-            // Box::pin(async { loop {} }) as Pin<Box<dyn Future<Output = ()>>>
-        },
-        |f| f,
-        |_| {},
-    );
-    // let g = unsafe { Pin::new_unchecked(fut) };
-
-    // cx.submit_task(fut);
-
-    todo!()
-};
+    let id = cx.scope_id();
+    cx.submit_task(Box::pin(async move { id }));
 
-const Task: FC<()> = |cx, props| {
-    let (_task, _res) = use_task(cx, || async { true });
-    // task.pause();
-    // task.restart();
-    // task.stop();
-    // task.drop();
-
-    //
-
-    let _s = use_task(cx, || async { "hello world".to_string() });
+    let (handle, contents) = use_task(cx, || async { "hello world".to_string() });
 
     todo!()
 };
-
-fn use_mut<P, T>(_cx: Context, _f: impl FnOnce() -> T) -> &mut T {
-    todo!()
-}

+ 1 - 1
packages/core/src/diff.rs

@@ -137,7 +137,7 @@ impl<'a> SavedDiffWork<'a> {
         std::mem::transmute(self)
     }
 
-    pub unsafe fn promote<'b>(self, vdom: &'b mut ResourcePool) -> DiffMachine<'b> {
+    pub unsafe fn promote<'b>(self, vdom: &'b ResourcePool) -> DiffMachine<'b> {
         let extended: SavedDiffWork<'b> = std::mem::transmute(self);
         DiffMachine {
             vdom,

+ 13 - 1
packages/core/src/hooks.rs

@@ -15,9 +15,21 @@ use std::{any::Any, cell::RefCell, future::Future, ops::Deref, rc::Rc};
 
 /// Awaits the given task, forcing the component to re-render when the value is ready.
 ///
+/// Returns the handle to the task and the value (if it is ready, else None).
 ///
+/// ```
+/// static Example: FC<()> = |cx, props| {
+///     let (task, value) = use_task(|| async {
+///         timer::sleep(Duration::from_secs(1)).await;
+///         "Hello World"
+///     });
 ///
-///
+///     match contents {
+///         Some(contents) => rsx!(cx, div { "{title}" }),
+///         None => rsx!(cx, div { "Loading..." }),
+///     }
+/// };
+/// ```
 pub fn use_task<'src, Out, Fut, Init>(
     cx: Context<'src>,
     task_initializer: Init,

+ 79 - 102
packages/core/src/scheduler.rs

@@ -101,6 +101,10 @@ pub enum SchedulerMsg {
     Immediate(ScopeId),
 
     // tasks
+    Task(TaskMsg),
+}
+
+pub enum TaskMsg {
     SubmitTask(FiberTask, u64),
     ToggleTask(u64),
     PauseTask(u64),
@@ -152,7 +156,9 @@ pub(crate) struct Scheduler {
 
     pub garbage_scopes: HashSet<ScopeId>,
 
-    pub lane: PriorityLane,
+    pub dirty_scopes: IndexSet<ScopeId>,
+    pub saved_state: Option<SavedDiffWork<'static>>,
+    pub in_progress: bool,
 }
 
 impl Scheduler {
@@ -181,7 +187,9 @@ impl Scheduler {
                     let task_id = task_counter.get();
                     task_counter.set(task_id + 1);
                     sender
-                        .unbounded_send(SchedulerMsg::SubmitTask(fiber_task, task_id))
+                        .unbounded_send(SchedulerMsg::Task(TaskMsg::SubmitTask(
+                            fiber_task, task_id,
+                        )))
                         .unwrap();
                     TaskHandle {
                         our_id: task_id,
@@ -214,6 +222,12 @@ impl Scheduler {
 
         let async_tasks = FuturesUnordered::new();
 
+        // push a task that would never resolve - prevents us from immediately aborting the scheduler
+        async_tasks.push(Box::pin(async {
+            std::future::pending::<()>().await;
+            ScopeId(0)
+        }) as FiberTask);
+
         Self {
             pool,
 
@@ -235,24 +249,9 @@ impl Scheduler {
 
             garbage_scopes: HashSet::new(),
 
-            // current_priority: EventPriority::Low,
-            lane: PriorityLane::new(),
-        }
-    }
-
-    pub fn manually_poll_events(&mut self) {
-        while let Ok(Some(msg)) = self.receiver.try_next() {
-            match msg {
-                SchedulerMsg::UiEvent(event) => self.ui_events.push_front(event),
-                SchedulerMsg::Immediate(im) => self.pending_immediates.push_front(im),
-
-                // task stuff
-                SchedulerMsg::SubmitTask(_, _) => todo!(),
-                SchedulerMsg::ToggleTask(_) => todo!(),
-                SchedulerMsg::PauseTask(_) => todo!(),
-                SchedulerMsg::ResumeTask(_) => todo!(),
-                SchedulerMsg::DropTask(_) => todo!(),
-            }
+            dirty_scopes: Default::default(),
+            saved_state: None,
+            in_progress: false,
         }
     }
 
@@ -272,31 +271,27 @@ impl Scheduler {
             }
         }
 
-        use EventPriority::*;
+        // use EventPriority::*;
 
-        match priority {
-            Immediate => todo!(),
-            High => todo!(),
-            Medium => todo!(),
-            Low => todo!(),
-        }
+        // match priority {
+        //     Immediate => todo!(),
+        //     High => todo!(),
+        //     Medium => todo!(),
+        //     Low => todo!(),
+        // }
 
         discrete
     }
 
     fn prepare_work(&mut self) {
-        while let Some(trigger) = self.ui_events.pop_back() {
-            if let Some(scope) = self.pool.get_scope_mut(trigger.scope) {}
-        }
+        // while let Some(trigger) = self.ui_events.pop_back() {
+        //     if let Some(scope) = self.pool.get_scope_mut(trigger.scope) {}
+        // }
     }
 
     // nothing to do, no events on channels, no work
     pub fn has_any_work(&self) -> bool {
-        self.lane.has_work() || self.has_pending_events()
-    }
-
-    pub fn has_pending_events(&self) -> bool {
-        !self.ui_events.is_empty()
+        !(self.dirty_scopes.is_empty() && self.ui_events.is_empty())
     }
 
     /// re-balance the work lanes, ensuring high-priority work properly bumps away low priority work
@@ -304,35 +299,39 @@ impl Scheduler {
 
     fn save_work(&mut self, lane: SavedDiffWork) {
         let saved: SavedDiffWork<'static> = unsafe { std::mem::transmute(lane) };
-        self.lane.saved_state = Some(saved);
+        self.saved_state = Some(saved);
     }
 
-    fn load_work(&mut self) -> SavedDiffWork<'static> {
-        // match self.current_priority {
-        //     EventPriority::Immediate => todo!(),
-        //     EventPriority::High => todo!(),
-        //     EventPriority::Medium => todo!(),
-        //     EventPriority::Low => todo!(),
-        // }
-        unsafe { self.lane.saved_state.take().unwrap().extend() }
+    unsafe fn load_work(&mut self) -> SavedDiffWork<'static> {
+        self.saved_state.take().unwrap().extend()
     }
 
     pub fn handle_channel_msg(&mut self, msg: SchedulerMsg) {
         match msg {
+            //
+            SchedulerMsg::Task(msg) => todo!(),
+
             SchedulerMsg::Immediate(_) => todo!(),
 
             SchedulerMsg::UiEvent(event) => {
                 //
 
-                self.handle_ui_event(event);
-            }
+                let (discrete, priority) = event_meta(&event);
 
-            //
-            SchedulerMsg::SubmitTask(_, _) => todo!(),
-            SchedulerMsg::ToggleTask(_) => todo!(),
-            SchedulerMsg::PauseTask(_) => todo!(),
-            SchedulerMsg::ResumeTask(_) => todo!(),
-            SchedulerMsg::DropTask(_) => todo!(),
+                if let Some(scope) = self.pool.get_scope_mut(event.scope) {
+                    if let Some(element) = event.mounted_dom_id {
+                        // TODO: bubble properly here
+                        scope.call_listener(event.event, element);
+
+                        while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
+                            //
+                            //     self.add_dirty_scope(dirty_scope, trigger.priority)
+                        }
+                    }
+                }
+
+                discrete;
+            }
         }
     }
 
@@ -346,22 +345,22 @@ impl Scheduler {
     ) -> bool {
         // Work through the current subtree, and commit the results when it finishes
         // When the deadline expires, give back the work
-        let saved_state = self.load_work();
+        let saved_state = unsafe { self.load_work() };
 
         // We have to split away some parts of ourself - current lane is borrowed mutably
-        let mut shared = self.pool.clone();
-        let mut machine = unsafe { saved_state.promote(&mut shared) };
+        let shared = self.pool.clone();
+        let mut machine = unsafe { saved_state.promote(&shared) };
 
         if machine.stack.is_empty() {
             let shared = self.pool.clone();
 
-            self.lane.dirty_scopes.sort_by(|a, b| {
+            self.dirty_scopes.sort_by(|a, b| {
                 let h1 = shared.get_scope(*a).unwrap().height;
                 let h2 = shared.get_scope(*b).unwrap().height;
                 h1.cmp(&h2)
             });
 
-            if let Some(scope) = self.lane.dirty_scopes.pop() {
+            if let Some(scope) = self.dirty_scopes.pop() {
                 let component = self.pool.get_scope(scope).unwrap();
                 let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
                 machine.stack.push(DiffInstruction::Diff { new, old });
@@ -378,7 +377,7 @@ impl Scheduler {
             false
         } else {
             for node in saved.seen_scopes.drain() {
-                self.lane.dirty_scopes.remove(&node);
+                self.dirty_scopes.remove(&node);
             }
 
             let mut new_mutations = Mutations::new();
@@ -395,9 +394,9 @@ impl Scheduler {
     /// Uses some fairly complex logic to schedule what work should be produced.
     ///
     /// Returns a list of successful mutations.
-    pub async fn work_with_deadline<'a>(
+    pub fn work_with_deadline<'a>(
         &'a mut self,
-        deadline: impl Future<Output = ()>,
+        mut deadline: impl FnMut() -> bool,
     ) -> Vec<Mutations<'a>> {
         /*
         Strategy:
@@ -429,33 +428,29 @@ impl Scheduler {
             - but if we received both - then we don't need to diff, do we? run as many as we can and then finally diff?
         */
         let mut committed_mutations = Vec::<Mutations<'static>>::new();
-        pin_mut!(deadline);
 
         loop {
-            // Move work out of the scheduler message receiver and into dedicated message lanes
-            self.manually_poll_events();
-
-            // Wait for any new events if we have nothing to do
-            // todo: poll the events once even if there is work to do to prevent starvation
-            if !self.has_any_work() {
-                futures_util::select! {
-                    // todo: find a solution for the exhausted queue problem
-                    // msg = self.async_tasks.next() => {}
-                    msg = self.receiver.next() => {
-                        log::debug!("received work in scheduler");
-                        self.handle_channel_msg(msg.unwrap())
-                    },
-                    _ = (&mut deadline).fuse() => return committed_mutations,
+            // switch our priority, pop off any work
+            for event in self.ui_events.drain(..) {
+                if let Some(scope) = self.pool.get_scope_mut(event.scope) {
+                    if let Some(element) = event.mounted_dom_id {
+                        // TODO: bubble properly here
+                        scope.call_listener(event.event, element);
+
+                        while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
+                            match dirty_scope {
+                                SchedulerMsg::Immediate(im) => {
+                                    self.dirty_scopes.insert(im);
+                                }
+                                _ => todo!(),
+                            }
+                        }
+                    }
                 }
             }
 
-            // switch our priority, pop off any work
-            self.prepare_work();
-
-            let mut deadline_reached = || (&mut deadline).now_or_never().is_some();
-
             let finished_before_deadline =
-                self.work_on_current_lane(&mut deadline_reached, &mut committed_mutations);
+                self.work_on_current_lane(&mut deadline, &mut committed_mutations);
 
             if !finished_before_deadline {
                 break;
@@ -471,7 +466,9 @@ impl Scheduler {
     pub fn work_sync<'a>(&'a mut self) -> Vec<Mutations<'a>> {
         let mut committed_mutations = Vec::new();
 
-        self.manually_poll_events();
+        while let Ok(Some(msg)) = self.receiver.try_next() {
+            self.handle_channel_msg(msg);
+        }
 
         if !self.has_any_work() {
             return committed_mutations;
@@ -534,23 +531,3 @@ impl Scheduler {
         }
     }
 }
-
-pub(crate) struct PriorityLane {
-    pub dirty_scopes: IndexSet<ScopeId>,
-    pub saved_state: Option<SavedDiffWork<'static>>,
-    pub in_progress: bool,
-}
-
-impl PriorityLane {
-    pub fn new() -> Self {
-        Self {
-            saved_state: None,
-            dirty_scopes: Default::default(),
-            in_progress: false,
-        }
-    }
-
-    fn has_work(&self) -> bool {
-        !self.dirty_scopes.is_empty() || self.in_progress
-    }
-}

+ 4 - 4
packages/core/src/tasks.rs

@@ -12,28 +12,28 @@ impl TaskHandle {
     /// This method is not synchronous - your task will not stop immediately.
     pub fn toggle(&self) {
         self.sender
-            .unbounded_send(SchedulerMsg::ToggleTask(self.our_id))
+            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
             .unwrap()
     }
 
     /// This method is not synchronous - your task will not stop immediately.
     pub fn resume(&self) {
         self.sender
-            .unbounded_send(SchedulerMsg::ResumeTask(self.our_id))
+            .unbounded_send(SchedulerMsg::Task(TaskMsg::ResumeTask(self.our_id)))
             .unwrap()
     }
 
     /// This method is not synchronous - your task will not stop immediately.
     pub fn stop(&self) {
         self.sender
-            .unbounded_send(SchedulerMsg::ToggleTask(self.our_id))
+            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
             .unwrap()
     }
 
     /// This method is not synchronous - your task will not stop immediately.
     pub fn restart(&self) {
         self.sender
-            .unbounded_send(SchedulerMsg::ToggleTask(self.our_id))
+            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
             .unwrap()
     }
 }

+ 33 - 10
packages/core/src/virtual_dom.rs

@@ -334,11 +334,8 @@ impl VirtualDom {
     /// applied the edits.
     ///
     /// Mutations are the only link between the RealDOM and the VirtualDOM.
-    pub async fn run_with_deadline(
-        &mut self,
-        deadline: impl Future<Output = ()>,
-    ) -> Vec<Mutations<'_>> {
-        self.scheduler.work_with_deadline(deadline).await
+    pub fn run_with_deadline(&mut self, deadline: impl FnMut() -> bool) -> Vec<Mutations<'_>> {
+        self.scheduler.work_with_deadline(deadline)
     }
 
     pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
@@ -355,12 +352,38 @@ impl VirtualDom {
 
         log::debug!("No active work.... waiting for some...");
         use futures_util::StreamExt;
+
+        // right now this won't poll events if there is ongoing work
+        // in the future we want to prioritize some events over ongoing work
+        // this is coming in the "priorities" PR
+
+        // Wait for any new events if we have nothing to do
+        // todo: poll the events once even if there is work to do to prevent starvation
         futures_util::select! {
-            // // hmm - will this resolve to none if there are no async tasks?
-            // _ = self.scheduler.async_tasks.next() => {
-            //     log::debug!("async task completed!");
-            // }
-            msg = self.scheduler.receiver.next() => self.scheduler.handle_channel_msg(msg.unwrap()),
+            _ = self.scheduler.async_tasks.next() => {}
+            msg = self.scheduler.receiver.next() => {
+                match msg.unwrap() {
+                    SchedulerMsg::Task(t) => todo!(),
+                    SchedulerMsg::Immediate(im) => {
+                        self.scheduler.dirty_scopes.insert(im);
+                    }
+                    SchedulerMsg::UiEvent(evt) => {
+                        self.scheduler.ui_events.push_back(evt);
+                    }
+                }
+            },
+        }
+
+        while let Ok(Some(msg)) = self.scheduler.receiver.try_next() {
+            match msg {
+                SchedulerMsg::Task(t) => todo!(),
+                SchedulerMsg::Immediate(im) => {
+                    self.scheduler.dirty_scopes.insert(im);
+                }
+                SchedulerMsg::UiEvent(evt) => {
+                    self.scheduler.ui_events.push_back(evt);
+                }
+            }
         }
     }
 }

+ 3 - 2
packages/web/src/lib.rs

@@ -61,6 +61,7 @@ use dioxus::prelude::Properties;
 use dioxus::virtual_dom::VirtualDom;
 pub use dioxus_core as dioxus;
 use dioxus_core::prelude::FC;
+use futures_util::FutureExt;
 
 mod cache;
 mod cfg;
@@ -145,10 +146,10 @@ pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T,
         dom.wait_for_work().await;
 
         // wait for the mainthread to schedule us in
-        let deadline = work_loop.wait_for_idle_time().await;
+        let mut deadline = work_loop.wait_for_idle_time().await;
 
         // run the virtualdom work phase until the frame deadline is reached
-        let mutations = dom.run_with_deadline(deadline).await;
+        let mutations = dom.run_with_deadline(|| (&mut deadline).now_or_never().is_some());
 
         // wait for the animation frame to fire so we can apply our changes
         work_loop.wait_for_raf().await;

+ 3 - 1
packages/web/src/ric_raf.rs

@@ -33,8 +33,10 @@ impl RafLoop {
             .call0(&JsValue::NULL)
             .unwrap();
 
+        let window = web_sys::window().unwrap();
+
         Self {
-            window: web_sys::window().unwrap(),
+            window,
             raf_receiver,
             raf_closure,
             ric_receiver,