123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- use crate::{
- innerlude::{LocalTask, SchedulerMsg},
- render_signal::RenderSignal,
- scope_context::Scope,
- scopes::ScopeId,
- Task,
- };
- use std::{
- cell::{Cell, Ref, RefCell},
- rc::Rc,
- };
- thread_local! {
- static RUNTIMES: RefCell<Vec<Rc<Runtime>>> = const { RefCell::new(vec![]) };
- }
- /// A global runtime that is shared across all scopes that provides the async runtime and context API
- pub struct Runtime {
- pub(crate) scope_states: RefCell<Vec<Option<Scope>>>,
- // We use this to track the current scope
- pub(crate) scope_stack: RefCell<Vec<ScopeId>>,
- // We use this to track the current task
- pub(crate) current_task: Cell<Option<Task>>,
- /// Tasks created with cx.spawn
- pub(crate) tasks: RefCell<slab::Slab<Rc<LocalTask>>>,
- // Currently suspended tasks
- pub(crate) suspended_tasks: Cell<usize>,
- pub(crate) rendering: Cell<bool>,
- pub(crate) sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
- // Synchronous tasks need to be run after the next render. The virtual dom stores a list of those tasks to send a signal to them when the next render is done.
- pub(crate) render_signal: RenderSignal,
- }
- impl Runtime {
- pub(crate) fn new(sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>) -> Rc<Self> {
- Rc::new(Self {
- sender,
- render_signal: RenderSignal::default(),
- rendering: Cell::new(true),
- scope_states: Default::default(),
- scope_stack: Default::default(),
- current_task: Default::default(),
- tasks: Default::default(),
- suspended_tasks: Default::default(),
- })
- }
- /// Get the current runtime
- pub fn current() -> Option<Rc<Self>> {
- RUNTIMES.with(|stack| stack.borrow().last().cloned())
- }
- /// Create a scope context. This slab is synchronized with the scope slab.
- pub(crate) fn create_scope(&self, context: Scope) {
- let id = context.id;
- let mut scopes = self.scope_states.borrow_mut();
- if scopes.len() <= id.0 {
- scopes.resize_with(id.0 + 1, Default::default);
- }
- scopes[id.0] = Some(context);
- }
- pub(crate) fn remove_scope(self: &Rc<Self>, id: ScopeId) {
- {
- let borrow = self.scope_states.borrow();
- if let Some(scope) = &borrow[id.0] {
- let _runtime_guard = RuntimeGuard::new(self.clone());
- // Manually drop tasks, hooks, and contexts inside of the runtime
- self.on_scope(id, || {
- // Drop all spawned tasks - order doesn't matter since tasks don't rely on eachother
- // In theory nested tasks might not like this
- for id in scope.spawned_tasks.take() {
- self.remove_task(id);
- }
- // Drop all hooks in reverse order in case a hook depends on another hook.
- for hook in scope.hooks.take().drain(..).rev() {
- drop(hook);
- }
- // Drop all contexts
- scope.shared_contexts.take();
- });
- }
- }
- self.scope_states.borrow_mut()[id.0].take();
- }
- /// Get the current scope id
- pub(crate) fn current_scope_id(&self) -> Option<ScopeId> {
- self.scope_stack.borrow().last().copied()
- }
- /// Call this function with the current scope set to the given scope
- ///
- /// Useful in a limited number of scenarios
- 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();
- }
- o
- }
- /// Get the state for any scope given its ID
- ///
- /// This is useful for inserting or removing contexts from a scope, or rendering out its root node
- pub(crate) fn get_state(&self, id: ScopeId) -> Option<Ref<'_, Scope>> {
- Ref::filter_map(self.scope_states.borrow(), |contexts| {
- contexts.get(id.0).and_then(|f| f.as_ref())
- })
- .ok()
- }
- /// Pushes a new scope onto the stack
- pub(crate) fn push(runtime: Rc<Runtime>) {
- RUNTIMES.with(|stack| stack.borrow_mut().push(runtime));
- }
- /// Pops a scope off the stack
- pub(crate) fn pop() {
- RUNTIMES.with(|stack| stack.borrow_mut().pop());
- }
- /// Runs a function with the current runtime
- pub(crate) fn with<R>(f: impl FnOnce(&Runtime) -> R) -> Option<R> {
- RUNTIMES.with(|stack| stack.borrow().last().map(|r| f(r)))
- }
- /// Runs a function with the current scope
- pub(crate) fn with_current_scope<R>(f: impl FnOnce(&Scope) -> R) -> Option<R> {
- Self::with(|rt| {
- rt.current_scope_id()
- .and_then(|scope| rt.get_state(scope).map(|sc| f(&sc)))
- })
- .flatten()
- }
- /// Runs a function with the current scope
- pub(crate) fn with_scope<R>(scope: ScopeId, f: impl FnOnce(&Scope) -> R) -> Option<R> {
- Self::with(|rt| rt.get_state(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.
- ///
- /// ```rust
- /// use dioxus::prelude::*;
- ///
- /// fn main() {
- /// let virtual_dom = VirtualDom::new(app);
- /// }
- ///
- /// fn app() -> Element {
- /// rsx!{ Component { runtime: Runtime::current().unwrap() } }
- /// }
- ///
- /// // In a dynamic library
- /// #[derive(Props, Clone)]
- /// struct ComponentProps {
- /// runtime: std::rc::Rc<Runtime>,
- /// }
- ///
- /// impl PartialEq for ComponentProps {
- /// fn eq(&self, _other: &Self) -> bool {
- /// true
- /// }
- /// }
- ///
- /// fn Component(cx: ComponentProps) -> Element {
- /// use_hook(|| {
- /// let _guard = RuntimeGuard::new(cx.runtime.clone());
- /// });
- ///
- /// rsx! { div {} }
- /// }
- /// ```
- 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 {
- Runtime::push(runtime);
- Self(())
- }
- }
- impl Drop for RuntimeGuard {
- fn drop(&mut self) {
- Runtime::pop();
- }
- }
|