Browse Source

Cleanup runtime code

Jonathan Kelley 1 year ago
parent
commit
3008870818

+ 3 - 7
packages/core/src/events.rs

@@ -1,4 +1,4 @@
-use crate::{global_context::current_scope_id, runtime::with_runtime, ScopeId};
+use crate::{global_context::current_scope_id, Runtime, ScopeId};
 use std::{
     cell::{Cell, RefCell},
     rc::Rc,
@@ -211,13 +211,9 @@ impl<T> EventHandler<T> {
     /// This borrows the event using a RefCell. Recursively calling a listener will cause a panic.
     pub fn call(&self, event: T) {
         if let Some(callback) = self.callback.borrow_mut().as_mut() {
-            with_runtime(|rt| {
-                rt.scope_stack.borrow_mut().push(self.origin);
-            });
+            Runtime::with(|rt| rt.scope_stack.borrow_mut().push(self.origin));
             callback(event);
-            with_runtime(|rt| {
-                rt.scope_stack.borrow_mut().pop();
-            });
+            Runtime::with(|rt| rt.scope_stack.borrow_mut().pop());
         }
     }
 

+ 19 - 22
packages/core/src/global_context.rs

@@ -2,37 +2,34 @@ use std::sync::Arc;
 
 use futures_util::Future;
 
-use crate::{
-    runtime::{with_current_scope, with_runtime},
-    Element, ScopeId, Task,
-};
+use crate::{runtime::Runtime, Element, ScopeId, Task};
 
 /// Get the current scope id
 pub fn current_scope_id() -> Option<ScopeId> {
-    with_runtime(|rt| rt.current_scope_id()).flatten()
+    Runtime::with(|rt| rt.current_scope_id()).flatten()
 }
 
 #[doc(hidden)]
 /// Check if the virtual dom is currently inside of the body of a component
 pub fn vdom_is_rendering() -> bool {
-    with_runtime(|rt| rt.rendering.get()).unwrap_or_default()
+    Runtime::with(|rt| rt.rendering.get()).unwrap_or_default()
 }
 
 /// Consume context from the current scope
 pub fn try_consume_context<T: 'static + Clone>() -> Option<T> {
-    with_current_scope(|cx| cx.consume_context::<T>()).flatten()
+    Runtime::with_current_scope(|cx| cx.consume_context::<T>()).flatten()
 }
 
 /// Consume context from the current scope
 pub fn consume_context<T: 'static + Clone>() -> T {
-    with_current_scope(|cx| cx.consume_context::<T>())
+    Runtime::with_current_scope(|cx| cx.consume_context::<T>())
         .flatten()
         .unwrap_or_else(|| panic!("Could not find context {}", std::any::type_name::<T>()))
 }
 
 /// Consume context from the current scope
 pub fn consume_context_from_scope<T: 'static + Clone>(scope_id: ScopeId) -> Option<T> {
-    with_runtime(|rt| {
+    Runtime::with(|rt| {
         rt.get_context(scope_id)
             .and_then(|cx| cx.consume_context::<T>())
     })
@@ -41,22 +38,22 @@ pub fn consume_context_from_scope<T: 'static + Clone>(scope_id: ScopeId) -> Opti
 
 /// Check if the current scope has a context
 pub fn has_context<T: 'static + Clone>() -> Option<T> {
-    with_current_scope(|cx| cx.has_context::<T>()).flatten()
+    Runtime::with_current_scope(|cx| cx.has_context::<T>()).flatten()
 }
 
 /// Provide context to the current scope
 pub fn provide_context<T: 'static + Clone>(value: T) -> T {
-    with_current_scope(|cx| cx.provide_context(value)).expect("to be in a dioxus runtime")
+    Runtime::with_current_scope(|cx| cx.provide_context(value)).expect("to be in a dioxus runtime")
 }
 
 /// Provide a context to the root scope
 pub fn provide_root_context<T: 'static + Clone>(value: T) -> Option<T> {
-    with_current_scope(|cx| cx.provide_root_context(value))
+    Runtime::with_current_scope(|cx| cx.provide_root_context(value))
 }
 
 /// Suspends the current component
 pub fn suspend() -> Option<Element> {
-    with_current_scope(|cx| {
+    Runtime::with_current_scope(|cx| {
         cx.suspend();
     });
     None
@@ -64,21 +61,21 @@ pub fn suspend() -> Option<Element> {
 
 /// Spawns the future but does not return the [`TaskId`]
 pub fn spawn(fut: impl Future<Output = ()> + 'static) -> Task {
-    with_current_scope(|cx| cx.spawn(fut)).expect("to be in a dioxus runtime")
+    Runtime::with_current_scope(|cx| cx.spawn(fut)).expect("to be in a dioxus runtime")
 }
 
 /// Spawn a future that Dioxus won't clean up when this component is unmounted
 ///
 /// This is good for tasks that need to be run after the component has been dropped.
 pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
-    with_current_scope(|cx| cx.spawn_forever(fut))
+    Runtime::with_current_scope(|cx| cx.spawn_forever(fut))
 }
 
 /// Informs the scheduler that this task is no longer needed and should be removed.
 ///
 /// This drops the task immediately.
 pub fn remove_future(id: Task) {
-    with_current_scope(|cx| cx.remove_future(id));
+    Runtime::with_current_scope(|cx| cx.remove_future(id));
 }
 
 /// Store a value between renders. The foundational hook for all other hooks.
@@ -98,24 +95,24 @@ pub fn remove_future(id: Task) {
 /// }
 /// ```
 pub fn use_hook<State: Clone + 'static>(initializer: impl FnOnce() -> State) -> State {
-    with_current_scope(|cx| cx.use_hook(initializer)).expect("to be in a dioxus runtime")
+    Runtime::with_current_scope(|cx| cx.use_hook(initializer)).expect("to be in a dioxus runtime")
 }
 
 /// Get the current render since the inception of this component
 ///
 /// This can be used as a helpful diagnostic when debugging hooks/renders, etc
 pub fn generation() -> usize {
-    with_current_scope(|cx| cx.generation()).expect("to be in a dioxus runtime")
+    Runtime::with_current_scope(|cx| cx.generation()).expect("to be in a dioxus runtime")
 }
 
 /// Get the parent of the current scope if it exists
 pub fn parent_scope() -> Option<ScopeId> {
-    with_current_scope(|cx| cx.parent_id()).flatten()
+    Runtime::with_current_scope(|cx| cx.parent_id()).flatten()
 }
 
 /// Mark the current scope as dirty, causing it to re-render
 pub fn needs_update() {
-    with_current_scope(|cx| cx.needs_update());
+    Runtime::with_current_scope(|cx| cx.needs_update());
 }
 
 /// Schedule an update for the current component
@@ -124,7 +121,7 @@ pub fn needs_update() {
 ///
 /// You should prefer [`schedule_update_any`] if you need to update multiple components.
 pub fn schedule_update() -> Arc<dyn Fn() + Send + Sync> {
-    with_current_scope(|cx| cx.schedule_update()).expect("to be in a dioxus runtime")
+    Runtime::with_current_scope(|cx| cx.schedule_update()).expect("to be in a dioxus runtime")
 }
 
 /// Schedule an update for any component given its [`ScopeId`].
@@ -133,5 +130,5 @@ pub fn schedule_update() -> Arc<dyn Fn() + Send + Sync> {
 ///
 /// Note: Unlike [`needs_update`], the function returned by this method will work outside of the dioxus runtime.
 pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
-    with_current_scope(|cx| cx.schedule_update_any()).expect("to be in a dioxus runtime")
+    Runtime::with_current_scope(|cx| cx.schedule_update_any()).expect("to be in a dioxus runtime")
 }

+ 2 - 2
packages/core/src/lib.rs

@@ -78,8 +78,8 @@ pub use crate::innerlude::{
     AnyValue, Attribute, AttributeValue, BoxedContext, CapturedError, Component, ComponentFunction,
     CrossPlatformConfig, DynamicNode, Element, ElementId, Event, Fragment, HasAttributes,
     IntoDynNode, Mutation, Mutations, NoOpMutations, PlatformBuilder, Properties, RenderReturn,
-    ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode,
-    VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
+    Runtime, ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, VComponent,
+    VNode, VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
 };
 
 /// The purpose of this module is to alleviate imports of many common types

+ 44 - 56
packages/core/src/runtime.rs

@@ -14,48 +14,6 @@ thread_local! {
     static RUNTIMES: RefCell<Vec<Rc<Runtime>>> = RefCell::new(vec![]);
 }
 
-/// Pushes a new scope onto the stack
-pub(crate) fn push_runtime(runtime: Rc<Runtime>) {
-    RUNTIMES.with(|stack| stack.borrow_mut().push(runtime));
-}
-
-/// Pops a scope off the stack
-pub(crate) fn pop_runtime() {
-    RUNTIMES.with(|stack| stack.borrow_mut().pop());
-}
-
-/// Runs a function with the current runtime
-pub(crate) fn with_runtime<F, R>(f: F) -> Option<R>
-where
-    F: FnOnce(&Runtime) -> R,
-{
-    RUNTIMES.with(|stack| {
-        let stack = stack.borrow();
-        stack.last().map(|r| f(r))
-    })
-}
-
-/// Runs a function with the current scope
-pub(crate) fn with_current_scope<F, R>(f: F) -> Option<R>
-where
-    F: FnOnce(&ScopeContext) -> R,
-{
-    with_runtime(|runtime| {
-        runtime
-            .current_scope_id()
-            .and_then(|scope| runtime.get_context(scope).map(|sc| f(&sc)))
-    })
-    .flatten()
-}
-
-/// Runs a function with the current scope
-pub(crate) fn with_scope<F, R>(scope: ScopeId, f: F) -> Option<R>
-where
-    F: FnOnce(&ScopeContext) -> R,
-{
-    with_runtime(|runtime| runtime.get_context(scope).map(|sc| f(&sc))).flatten()
-}
-
 /// A global runtime that is shared across all scopes that provides the async runtime and context API
 pub struct Runtime {
     pub(crate) scope_contexts: RefCell<Vec<Option<ScopeContext>>>,
@@ -112,7 +70,7 @@ impl Runtime {
     /// Call this function with the current scope set to the given scope
     ///
     /// Useful in a limited number of scenarios, not public.
-    pub(crate) fn with_scope<O>(&self, id: ScopeId, f: impl FnOnce() -> O) -> O {
+    pub fn on_scope<O>(&self, id: ScopeId, f: impl FnOnce() -> O) -> O {
         self.scope_stack.borrow_mut().push(id);
         let o = f();
         self.scope_stack.borrow_mut().pop();
@@ -128,6 +86,47 @@ impl Runtime {
         })
         .ok()
     }
+
+    /// Pushes a new scope onto the stack
+    pub(crate) fn push_runtime(runtime: Rc<Runtime>) {
+        RUNTIMES.with(|stack| stack.borrow_mut().push(runtime));
+    }
+
+    /// Pops a scope off the stack
+    pub(crate) fn pop_runtime() {
+        RUNTIMES.with(|stack| stack.borrow_mut().pop());
+    }
+
+    /// Runs a function with the current runtime
+    pub(crate) fn with<F, R>(f: F) -> Option<R>
+    where
+        F: FnOnce(&Runtime) -> R,
+    {
+        RUNTIMES.with(|stack| {
+            let stack = stack.borrow();
+            stack.last().map(|r| f(r))
+        })
+    }
+
+    /// Runs a function with the current scope
+    pub(crate) fn with_current_scope<F, R>(f: F) -> Option<R>
+    where
+        F: FnOnce(&ScopeContext) -> R,
+    {
+        Self::with(|rt| {
+            rt.current_scope_id()
+                .and_then(|scope| rt.get_context(scope).map(|sc| f(&sc)))
+        })
+        .flatten()
+    }
+
+    /// Runs a function with the current scope
+    pub(crate) fn with_scope<F, R>(scope: ScopeId, f: F) -> Option<R>
+    where
+        F: FnOnce(&ScopeContext) -> R,
+    {
+        Self::with(|rt| rt.get_context(scope).map(|sc| f(&sc))).flatten()
+    }
 }
 
 /// A guard for a new runtime. This must be used to override the current runtime when importing components from a dynamic library that has it's own runtime.
@@ -166,24 +165,13 @@ pub struct RuntimeGuard(());
 impl RuntimeGuard {
     /// Create a new runtime guard that sets the current Dioxus runtime. The runtime will be reset when the guard is dropped
     pub fn new(runtime: Rc<Runtime>) -> Self {
-        push_runtime(runtime);
+        Runtime::push_runtime(runtime);
         Self(())
     }
-
-    /// Run a function with a given runtime and scope in context
-    pub fn with<O>(runtime: Rc<Runtime>, scope: Option<ScopeId>, f: impl FnOnce() -> O) -> O {
-        let guard = Self::new(runtime.clone());
-        let o = match scope {
-            Some(scope) => Runtime::with_scope(&runtime, scope, f),
-            None => f(),
-        };
-        drop(guard);
-        o
-    }
 }
 
 impl Drop for RuntimeGuard {
     fn drop(&mut self) {
-        pop_runtime();
+        Runtime::pop_runtime();
     }
 }

+ 23 - 26
packages/core/src/scope_context.rs

@@ -1,8 +1,4 @@
-use crate::{
-    innerlude::SchedulerMsg,
-    runtime::{with_runtime, with_scope},
-    Element, ScopeId, Task,
-};
+use crate::{innerlude::SchedulerMsg, Element, Runtime, ScopeId, Task};
 use rustc_hash::FxHashSet;
 use std::{
     any::Any,
@@ -53,7 +49,7 @@ impl ScopeContext {
     }
 
     fn sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
-        with_runtime(|rt| rt.sender.clone()).unwrap()
+        Runtime::with(|rt| rt.sender.clone()).unwrap()
     }
 
     /// Mark this scope as dirty, and schedule a render for it.
@@ -107,7 +103,7 @@ impl ScopeContext {
         }
 
         let mut search_parent = self.parent_id;
-        let cur_runtime = with_runtime(|runtime: &crate::runtime::Runtime| {
+        let cur_runtime = Runtime::with(|runtime| {
             while let Some(parent_id) = search_parent {
                 let parent = runtime.get_context(parent_id).unwrap();
                 tracing::trace!(
@@ -210,7 +206,7 @@ impl ScopeContext {
     /// Note that you should be checking if the context existed before trying to provide a new one. Providing a context
     /// when a context already exists will swap the context out for the new one, which may not be what you want.
     pub fn provide_root_context<T: 'static + Clone>(&self, context: T) -> T {
-        with_runtime(|runtime| {
+        Runtime::with(|runtime| {
             runtime
                 .get_context(ScopeId::ROOT)
                 .unwrap()
@@ -221,7 +217,7 @@ impl ScopeContext {
 
     /// Spawns the future but does not return the [`TaskId`]
     pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) -> Task {
-        let id = with_runtime(|rt| rt.spawn(self.id, fut)).expect("Runtime to exist");
+        let id = Runtime::with(|rt| rt.spawn(self.id, fut)).expect("Runtime to exist");
         self.spawned_tasks.borrow_mut().insert(id);
         id
     }
@@ -231,14 +227,14 @@ impl ScopeContext {
     /// This is good for tasks that need to be run after the component has been dropped.
     pub fn spawn_forever(&self, fut: impl Future<Output = ()> + 'static) -> Task {
         // The root scope will never be unmounted so we can just add the task at the top of the app
-        with_runtime(|rt| rt.spawn(ScopeId::ROOT, fut)).expect("Runtime to exist")
+        Runtime::with(|rt| rt.spawn(ScopeId::ROOT, fut)).expect("Runtime to exist")
     }
 
     /// Informs the scheduler that this task is no longer needed and should be removed.
     ///
     /// This drops the task immediately.
     pub fn remove_future(&self, id: Task) {
-        with_runtime(|rt| rt.remove_task(id)).expect("Runtime to exist");
+        Runtime::with(|rt| rt.remove_task(id)).expect("Runtime to exist");
     }
 
     /// Mark this component as suspended and then return None
@@ -303,7 +299,7 @@ impl ScopeContext {
 impl Drop for ScopeContext {
     fn drop(&mut self) {
         // Drop all spawned tasks
-        _ = with_runtime(|rt| {
+        _ = Runtime::with(|rt| {
             for id in self.spawned_tasks.borrow().iter() {
                 rt.remove_task(*id);
             }
@@ -314,23 +310,23 @@ impl Drop for ScopeContext {
 impl ScopeId {
     /// Get the current scope id
     pub fn current_scope_id(self) -> Option<ScopeId> {
-        with_runtime(|rt| rt.current_scope_id()).flatten()
+        Runtime::with(|rt| rt.current_scope_id()).flatten()
     }
 
     #[doc(hidden)]
     /// Check if the virtual dom is currently inside of the body of a component
     pub fn vdom_is_rendering(self) -> bool {
-        with_runtime(|rt| rt.rendering.get()).unwrap_or_default()
+        Runtime::with(|rt| rt.rendering.get()).unwrap_or_default()
     }
 
     /// Consume context from the current scope
     pub fn consume_context<T: 'static + Clone>(self) -> Option<T> {
-        with_scope(self, |cx| cx.consume_context::<T>()).flatten()
+        Runtime::with_scope(self, |cx| cx.consume_context::<T>()).flatten()
     }
 
     /// Consume context from the current scope
     pub fn consume_context_from_scope<T: 'static + Clone>(self, scope_id: ScopeId) -> Option<T> {
-        with_runtime(|rt| {
+        Runtime::with(|rt| {
             rt.get_context(scope_id)
                 .and_then(|cx| cx.consume_context::<T>())
         })
@@ -339,17 +335,18 @@ impl ScopeId {
 
     /// Check if the current scope has a context
     pub fn has_context<T: 'static + Clone>(self) -> Option<T> {
-        with_scope(self, |cx| cx.has_context::<T>()).flatten()
+        Runtime::with_scope(self, |cx| cx.has_context::<T>()).flatten()
     }
 
     /// Provide context to the current scope
     pub fn provide_context<T: 'static + Clone>(self, value: T) -> T {
-        with_scope(self, |cx| cx.provide_context(value)).expect("to be in a dioxus runtime")
+        Runtime::with_scope(self, |cx| cx.provide_context(value))
+            .expect("to be in a dioxus runtime")
     }
 
     /// Suspends the current component
     pub fn suspend(self) -> Option<Element> {
-        with_scope(self, |cx| {
+        Runtime::with_scope(self, |cx| {
             cx.suspend();
         });
         None
@@ -357,40 +354,40 @@ impl ScopeId {
 
     /// Pushes the future onto the poll queue to be polled after the component renders.
     pub fn push_future(self, fut: impl Future<Output = ()> + 'static) -> Option<Task> {
-        with_scope(self, |cx| cx.spawn(fut))
+        Runtime::with_scope(self, |cx| cx.spawn(fut))
     }
 
     /// Spawns the future but does not return the [`TaskId`]
     pub fn spawn(self, fut: impl Future<Output = ()> + 'static) {
-        with_scope(self, |cx| cx.spawn(fut));
+        Runtime::with_scope(self, |cx| cx.spawn(fut));
     }
 
     /// Get the current render since the inception of this component
     ///
     /// This can be used as a helpful diagnostic when debugging hooks/renders, etc
     pub fn generation(self) -> Option<usize> {
-        with_scope(self, |cx| Some(cx.generation())).expect("to be in a dioxus runtime")
+        Runtime::with_scope(self, |cx| Some(cx.generation())).expect("to be in a dioxus runtime")
     }
 
     /// Get the parent of the current scope if it exists
     pub fn parent_scope(self) -> Option<ScopeId> {
-        with_scope(self, |cx| cx.parent_id()).flatten()
+        Runtime::with_scope(self, |cx| cx.parent_id()).flatten()
     }
 
     /// Mark the current scope as dirty, causing it to re-render
     pub fn needs_update(self) {
-        with_scope(self, |cx| cx.needs_update());
+        Runtime::with_scope(self, |cx| cx.needs_update());
     }
 
     /// Create a subscription that schedules a future render for the reference component. Unlike [`Self::needs_update`], this function will work outside of the dioxus runtime.
     ///
     /// ## Notice: you should prefer using [`dioxus_core::schedule_update_any`] and [`Self::scope_id`]
     pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
-        with_scope(*self, |cx| cx.schedule_update()).expect("to be in a dioxus runtime")
+        Runtime::with_scope(*self, |cx| cx.schedule_update()).expect("to be in a dioxus runtime")
     }
 
     /// Get the height of the current scope
     pub fn height(self) -> u32 {
-        with_scope(self, |cx| cx.height()).expect("to be in a dioxus runtime")
+        Runtime::with_scope(self, |cx| cx.height()).expect("to be in a dioxus runtime")
     }
 }

+ 6 - 5
packages/desktop/src/assets.rs

@@ -36,11 +36,12 @@ impl AssetHandlerRegistry {
         responder: RequestAsyncResponder,
     ) {
         if let Some(handler) = self.handlers.borrow().get(name) {
-            // make sure the runtime is alive for the duration of the handler
-            // We should do this for all the things - not just asset handlers
-            RuntimeGuard::with(self.dom_rt.clone(), Some(handler.scope), || {
-                (handler.f)(request, responder)
-            });
+            // Push the runtime onto the stack
+            let _guard = RuntimeGuard::new(self.dom_rt.clone());
+
+            // And run the handler in the scope of the component that created it
+            self.dom_rt
+                .on_scope(handler.scope, || (handler.f)(request, responder));
         }
     }