runtime.rs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. use std::cell::{Cell, Ref, RefCell};
  2. use crate::{innerlude::Scheduler, scope_context::ScopeContext, scopes::ScopeId};
  3. use std::rc::Rc;
  4. thread_local! {
  5. static RUNTIMES: RefCell<Vec<Rc<Runtime>>> = RefCell::new(vec![]);
  6. }
  7. /// Pushes a new scope onto the stack
  8. pub(crate) fn push_runtime(runtime: Rc<Runtime>) {
  9. RUNTIMES.with(|stack| stack.borrow_mut().push(runtime));
  10. }
  11. /// Pops a scope off the stack
  12. pub(crate) fn pop_runtime() {
  13. RUNTIMES.with(|stack| stack.borrow_mut().pop());
  14. }
  15. /// Runs a function with the current runtime
  16. pub(crate) fn with_runtime<F, R>(f: F) -> Option<R>
  17. where
  18. F: FnOnce(&Runtime) -> R,
  19. {
  20. RUNTIMES.with(|stack| {
  21. let stack = stack.borrow();
  22. stack.last().map(|r| f(r))
  23. })
  24. }
  25. /// Runs a function with the current scope
  26. pub(crate) fn with_current_scope<F, R>(f: F) -> Option<R>
  27. where
  28. F: FnOnce(&ScopeContext) -> R,
  29. {
  30. with_runtime(|runtime| {
  31. runtime
  32. .current_scope_id()
  33. .and_then(|scope| runtime.get_context(scope).map(|sc| f(&sc)))
  34. })
  35. .flatten()
  36. }
  37. /// A global runtime that is shared across all scopes that provides the async runtime and context API
  38. pub struct Runtime {
  39. pub(crate) scope_contexts: RefCell<Vec<Option<ScopeContext>>>,
  40. pub(crate) scheduler: Rc<Scheduler>,
  41. // We use this to track the current scope
  42. pub(crate) scope_stack: RefCell<Vec<ScopeId>>,
  43. pub(crate) rendering: Cell<bool>,
  44. }
  45. impl Runtime {
  46. pub(crate) fn new(scheduler: Rc<Scheduler>) -> Rc<Self> {
  47. Rc::new(Self {
  48. scheduler,
  49. scope_contexts: Default::default(),
  50. scope_stack: Default::default(),
  51. rendering: Cell::new(true),
  52. })
  53. }
  54. /// Get the current runtime
  55. pub fn current() -> Option<Rc<Self>> {
  56. RUNTIMES.with(|stack| stack.borrow().last().cloned())
  57. }
  58. /// Create a scope context. This slab is synchronized with the scope slab.
  59. pub(crate) fn create_context_at(&self, id: ScopeId, context: ScopeContext) {
  60. let mut contexts = self.scope_contexts.borrow_mut();
  61. if contexts.len() <= id.0 {
  62. contexts.resize_with(id.0 + 1, Default::default);
  63. }
  64. contexts[id.0] = Some(context);
  65. }
  66. pub(crate) fn remove_context(&self, id: ScopeId) {
  67. self.scope_contexts.borrow_mut()[id.0] = None;
  68. }
  69. /// Get the current scope id
  70. pub(crate) fn current_scope_id(&self) -> Option<ScopeId> {
  71. self.scope_stack.borrow().last().copied()
  72. }
  73. /// Call this function with the current scope set to the given scope
  74. ///
  75. /// Useful in a limited number of scenarios, not public.
  76. pub(crate) fn with_scope<O>(&self, id: ScopeId, f: impl FnOnce() -> O) -> O {
  77. self.scope_stack.borrow_mut().push(id);
  78. let o = f();
  79. self.scope_stack.borrow_mut().pop();
  80. o
  81. }
  82. /// Get the context for any scope given its ID
  83. ///
  84. /// This is useful for inserting or removing contexts from a scope, or rendering out its root node
  85. pub(crate) fn get_context(&self, id: ScopeId) -> Option<Ref<'_, ScopeContext>> {
  86. Ref::filter_map(self.scope_contexts.borrow(), |contexts| {
  87. contexts.get(id.0).and_then(|f| f.as_ref())
  88. })
  89. .ok()
  90. }
  91. }
  92. /// 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.
  93. ///
  94. /// ```rust
  95. /// use dioxus::prelude::*;
  96. ///
  97. /// fn main() {
  98. /// let virtual_dom = VirtualDom::new(app);
  99. /// }
  100. ///
  101. /// fn app(cx: Scope) -> Element {
  102. /// render!{ Component { runtime: Runtime::current().unwrap() } }
  103. /// }
  104. ///
  105. /// // In a dynamic library
  106. /// #[derive(Props)]
  107. /// struct ComponentProps {
  108. /// runtime: std::rc::Rc<Runtime>,
  109. /// }
  110. ///
  111. /// impl PartialEq for ComponentProps {
  112. /// fn eq(&self, _other: &Self) -> bool {
  113. /// true
  114. /// }
  115. /// }
  116. ///
  117. /// # #[allow(non_snake_case)]
  118. /// fn Component(cx: Scope<ComponentProps>) -> Element {
  119. /// cx.use_hook(|| RuntimeGuard::new(cx.props.runtime.clone()));
  120. ///
  121. /// render! { div {} }
  122. /// }
  123. /// ```
  124. pub struct RuntimeGuard(Rc<Runtime>);
  125. impl RuntimeGuard {
  126. /// Create a new runtime guard that sets the current Dioxus runtime. The runtime will be reset when the guard is dropped
  127. pub fn new(runtime: Rc<Runtime>) -> Self {
  128. push_runtime(runtime.clone());
  129. Self(runtime)
  130. }
  131. /// Run a function with a given runtime and scope in context
  132. pub fn with<O>(runtime: Rc<Runtime>, scope: Option<ScopeId>, f: impl FnOnce() -> O) -> O {
  133. let guard = Self::new(runtime.clone());
  134. let o = match scope {
  135. Some(scope) => Runtime::with_scope(&runtime, scope, f),
  136. None => f(),
  137. };
  138. drop(guard);
  139. o
  140. }
  141. }
  142. impl Drop for RuntimeGuard {
  143. fn drop(&mut self) {
  144. pop_runtime();
  145. }
  146. }