runtime.rs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. use crate::{
  2. innerlude::{LocalTask, SchedulerMsg},
  3. render_signal::RenderSignal,
  4. scope_context::Scope,
  5. scopes::ScopeId,
  6. Task,
  7. };
  8. use std::{
  9. cell::{Cell, Ref, RefCell},
  10. rc::Rc,
  11. };
  12. thread_local! {
  13. static RUNTIMES: RefCell<Vec<Rc<Runtime>>> = const { RefCell::new(vec![]) };
  14. }
  15. /// A global runtime that is shared across all scopes that provides the async runtime and context API
  16. pub struct Runtime {
  17. pub(crate) scope_states: RefCell<Vec<Option<Scope>>>,
  18. // We use this to track the current scope
  19. pub(crate) scope_stack: RefCell<Vec<ScopeId>>,
  20. // We use this to track the current task
  21. pub(crate) current_task: Cell<Option<Task>>,
  22. /// Tasks created with cx.spawn
  23. pub(crate) tasks: RefCell<slab::Slab<Rc<LocalTask>>>,
  24. // Currently suspended tasks
  25. pub(crate) suspended_tasks: Cell<usize>,
  26. pub(crate) rendering: Cell<bool>,
  27. pub(crate) sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
  28. // 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.
  29. pub(crate) render_signal: RenderSignal,
  30. }
  31. impl Runtime {
  32. pub(crate) fn new(sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>) -> Rc<Self> {
  33. Rc::new(Self {
  34. sender,
  35. render_signal: RenderSignal::default(),
  36. rendering: Cell::new(true),
  37. scope_states: Default::default(),
  38. scope_stack: Default::default(),
  39. current_task: Default::default(),
  40. tasks: Default::default(),
  41. suspended_tasks: Default::default(),
  42. })
  43. }
  44. /// Get the current runtime
  45. pub fn current() -> Option<Rc<Self>> {
  46. RUNTIMES.with(|stack| stack.borrow().last().cloned())
  47. }
  48. /// Create a scope context. This slab is synchronized with the scope slab.
  49. pub(crate) fn create_scope(&self, context: Scope) {
  50. let id = context.id;
  51. let mut scopes = self.scope_states.borrow_mut();
  52. if scopes.len() <= id.0 {
  53. scopes.resize_with(id.0 + 1, Default::default);
  54. }
  55. scopes[id.0] = Some(context);
  56. }
  57. pub(crate) fn remove_scope(self: &Rc<Self>, id: ScopeId) {
  58. {
  59. let borrow = self.scope_states.borrow();
  60. if let Some(scope) = &borrow[id.0] {
  61. let _runtime_guard = RuntimeGuard::new(self.clone());
  62. // Manually drop tasks, hooks, and contexts inside of the runtime
  63. self.on_scope(id, || {
  64. // Drop all spawned tasks - order doesn't matter since tasks don't rely on eachother
  65. // In theory nested tasks might not like this
  66. for id in scope.spawned_tasks.take() {
  67. self.remove_task(id);
  68. }
  69. // Drop all hooks in reverse order in case a hook depends on another hook.
  70. for hook in scope.hooks.take().drain(..).rev() {
  71. drop(hook);
  72. }
  73. // Drop all contexts
  74. scope.shared_contexts.take();
  75. });
  76. }
  77. }
  78. self.scope_states.borrow_mut()[id.0].take();
  79. }
  80. /// Get the current scope id
  81. pub(crate) fn current_scope_id(&self) -> Option<ScopeId> {
  82. self.scope_stack.borrow().last().copied()
  83. }
  84. /// Call this function with the current scope set to the given scope
  85. ///
  86. /// Useful in a limited number of scenarios
  87. pub fn on_scope<O>(&self, id: ScopeId, f: impl FnOnce() -> O) -> O {
  88. {
  89. self.scope_stack.borrow_mut().push(id);
  90. }
  91. let o = f();
  92. {
  93. self.scope_stack.borrow_mut().pop();
  94. }
  95. o
  96. }
  97. /// Get the state for any scope given its ID
  98. ///
  99. /// This is useful for inserting or removing contexts from a scope, or rendering out its root node
  100. pub(crate) fn get_state(&self, id: ScopeId) -> Option<Ref<'_, Scope>> {
  101. Ref::filter_map(self.scope_states.borrow(), |contexts| {
  102. contexts.get(id.0).and_then(|f| f.as_ref())
  103. })
  104. .ok()
  105. }
  106. /// Pushes a new scope onto the stack
  107. pub(crate) fn push(runtime: Rc<Runtime>) {
  108. RUNTIMES.with(|stack| stack.borrow_mut().push(runtime));
  109. }
  110. /// Pops a scope off the stack
  111. pub(crate) fn pop() {
  112. RUNTIMES.with(|stack| stack.borrow_mut().pop());
  113. }
  114. /// Runs a function with the current runtime
  115. pub(crate) fn with<R>(f: impl FnOnce(&Runtime) -> R) -> Option<R> {
  116. RUNTIMES.with(|stack| stack.borrow().last().map(|r| f(r)))
  117. }
  118. /// Runs a function with the current scope
  119. pub(crate) fn with_current_scope<R>(f: impl FnOnce(&Scope) -> R) -> Option<R> {
  120. Self::with(|rt| {
  121. rt.current_scope_id()
  122. .and_then(|scope| rt.get_state(scope).map(|sc| f(&sc)))
  123. })
  124. .flatten()
  125. }
  126. /// Runs a function with the current scope
  127. pub(crate) fn with_scope<R>(scope: ScopeId, f: impl FnOnce(&Scope) -> R) -> Option<R> {
  128. Self::with(|rt| rt.get_state(scope).map(|sc| f(&sc))).flatten()
  129. }
  130. }
  131. /// 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.
  132. ///
  133. /// ```rust
  134. /// use dioxus::prelude::*;
  135. ///
  136. /// fn main() {
  137. /// let virtual_dom = VirtualDom::new(app);
  138. /// }
  139. ///
  140. /// fn app() -> Element {
  141. /// rsx!{ Component { runtime: Runtime::current().unwrap() } }
  142. /// }
  143. ///
  144. /// // In a dynamic library
  145. /// #[derive(Props, Clone)]
  146. /// struct ComponentProps {
  147. /// runtime: std::rc::Rc<Runtime>,
  148. /// }
  149. ///
  150. /// impl PartialEq for ComponentProps {
  151. /// fn eq(&self, _other: &Self) -> bool {
  152. /// true
  153. /// }
  154. /// }
  155. ///
  156. /// fn Component(cx: ComponentProps) -> Element {
  157. /// use_hook(|| {
  158. /// let _guard = RuntimeGuard::new(cx.runtime.clone());
  159. /// });
  160. ///
  161. /// rsx! { div {} }
  162. /// }
  163. /// ```
  164. pub struct RuntimeGuard(());
  165. impl RuntimeGuard {
  166. /// Create a new runtime guard that sets the current Dioxus runtime. The runtime will be reset when the guard is dropped
  167. pub fn new(runtime: Rc<Runtime>) -> Self {
  168. Runtime::push(runtime);
  169. Self(())
  170. }
  171. }
  172. impl Drop for RuntimeGuard {
  173. fn drop(&mut self) {
  174. Runtime::pop();
  175. }
  176. }