fix scope context

This commit is contained in:
Evan Almloff 2023-08-04 14:12:59 -07:00
parent 04cdb14e5b
commit cb4c46154d
7 changed files with 83 additions and 45 deletions

View file

@ -3,15 +3,13 @@
//! The example from the README.md. //! The example from the README.md.
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_signals::{use_init_signal_rt, use_signal}; use dioxus_signals::use_signal;
fn main() { fn main() {
dioxus_desktop::launch(app); dioxus_desktop::launch(app);
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
use_init_signal_rt(cx);
let mut count = use_signal(cx, || 0); let mut count = use_signal(cx, || 0);
use_future!(cx, || async move { use_future!(cx, || async move {

View file

@ -110,11 +110,13 @@ impl VirtualDom {
// Drop all the hooks once the children are dropped // Drop all the hooks once the children are dropped
// this means we'll drop hooks bottom-up // this means we'll drop hooks bottom-up
scope.hooks.get_mut().clear(); scope.hooks.get_mut().clear();
let context = scope.context(); {
let context = scope.context();
// Drop all the futures once the hooks are dropped // Drop all the futures once the hooks are dropped
for task_id in context.spawned_tasks.borrow_mut().drain() { for task_id in context.spawned_tasks.borrow_mut().drain() {
context.tasks.remove(task_id); context.tasks.remove(task_id);
}
} }
self.scopes.remove(id.0); self.scopes.remove(id.0);

View file

@ -209,7 +209,7 @@ impl<'b> VirtualDom {
self.diff_scope(scope_id); self.diff_scope(scope_id);
self.dirty_scopes.remove(&DirtyScope { self.dirty_scopes.remove(&DirtyScope {
height: self.runtime.scope_contexts[scope_id.0].height, height: self.runtime.get_context(scope_id).unwrap().height,
id: scope_id, id: scope_id,
}); });
} }

View file

@ -1,20 +1,14 @@
use std::cell::RefCell; use std::cell::{Ref, RefCell};
use crate::{ use crate::{innerlude::Scheduler, scope_context::ScopeContext, scopes::ScopeId};
innerlude::{DirtyScope, Scheduler, SchedulerMsg}, use std::rc::Rc;
scope_context::ScopeContext,
scopes::{ScopeId, ScopeState},
};
use rustc_hash::FxHashSet;
use slab::Slab;
use std::{collections::BTreeSet, rc::Rc};
thread_local! { thread_local! {
static RUNTIMES: RefCell<Vec<Runtime>> = RefCell::new(vec![]); static RUNTIMES: RefCell<Vec<Rc<Runtime>>> = RefCell::new(vec![]);
} }
/// Pushes a new scope onto the stack /// Pushes a new scope onto the stack
pub(crate) fn push_runtime(runtime: Runtime) { pub(crate) fn push_runtime(runtime: Rc<Runtime>) {
RUNTIMES.with(|stack| stack.borrow_mut().push(runtime)); RUNTIMES.with(|stack| stack.borrow_mut().push(runtime));
} }
@ -30,7 +24,7 @@ where
{ {
RUNTIMES.with(|stack| { RUNTIMES.with(|stack| {
let stack = stack.borrow(); let stack = stack.borrow();
stack.last().map(f) stack.last().map(|r| f(&**r))
}) })
} }
@ -42,28 +36,50 @@ where
with_runtime(|runtime| { with_runtime(|runtime| {
runtime runtime
.current_scope_id() .current_scope_id()
.and_then(|scope| runtime.get_context(scope).map(f)) .and_then(|scope| runtime.get_context(scope).map(|sc| f(&*sc)))
}) })
.flatten() .flatten()
} }
pub struct Runtime { pub struct Runtime {
pub(crate) scope_contexts: Slab<ScopeContext>, pub(crate) scope_contexts: RefCell<Vec<Option<ScopeContext>>>,
pub(crate) scheduler: Rc<Scheduler>, pub(crate) scheduler: Rc<Scheduler>,
// While diffing we need some sort of way of breaking off a stream of suspended mutations. // While diffing we need some sort of way of breaking off a stream of suspended mutations.
pub(crate) scope_stack: RefCell<Vec<ScopeId>>, pub(crate) scope_stack: RefCell<Vec<ScopeId>>,
} }
impl Drop for Runtime {
fn drop(&mut self) {
// todo: do this better
pop_runtime();
}
}
impl Runtime { impl Runtime {
pub(crate) fn new(scheduler: Rc<Scheduler>) -> Rc<Self> { pub(crate) fn new(scheduler: Rc<Scheduler>) -> Rc<Self> {
Rc::new(Self { let runtime = Rc::new(Self {
scheduler, scheduler,
scope_contexts: Default::default(), scope_contexts: Default::default(),
scope_stack: Default::default(), scope_stack: Default::default(),
}) });
push_runtime(runtime.clone());
runtime
}
/// Create a scope context. This slab is synchronized with the scope slab.
pub(crate) fn create_context_at(&self, id: ScopeId, context: ScopeContext) {
let mut contexts = self.scope_contexts.borrow_mut();
if contexts.len() <= id.0 {
contexts.resize_with(id.0 + 1, Default::default);
}
contexts[id.0] = Some(context);
}
pub(crate) fn remove_context(&self, id: ScopeId) {
self.scope_contexts.borrow_mut()[id.0] = None;
} }
/// Get the current scope id /// Get the current scope id
@ -71,17 +87,13 @@ impl Runtime {
self.scope_stack.borrow().last().copied() self.scope_stack.borrow().last().copied()
} }
/// Get the state for any scope given its ID /// Get the context for any scope given its ID
/// ///
/// This is useful for inserting or removing contexts from a scope, or rendering out its root node /// This is useful for inserting or removing contexts from a scope, or rendering out its root node
pub fn get_context(&self, id: ScopeId) -> Option<&ScopeContext> { pub fn get_context(&self, id: ScopeId) -> Option<Ref<'_, ScopeContext>> {
self.scope_contexts.get(id.0).map(|f| &*f) Ref::filter_map(self.scope_contexts.borrow(), |contexts| {
} contexts.get(id.0).and_then(|f| f.as_ref())
})
/// Get the single scope at the top of the Runtime tree that will always be around .ok()
///
/// This scope has a ScopeId of 0 and is the root of the tree
pub fn base_context(&self) -> &ScopeContext {
self.get_context(ScopeId(0)).unwrap()
} }
} }

View file

@ -3,6 +3,7 @@ use crate::{
bump_frame::BumpFrame, bump_frame::BumpFrame,
innerlude::DirtyScope, innerlude::DirtyScope,
nodes::RenderReturn, nodes::RenderReturn,
scope_context::ScopeContext,
scopes::{ScopeId, ScopeState}, scopes::{ScopeId, ScopeState},
virtual_dom::VirtualDom, virtual_dom::VirtualDom,
}; };
@ -20,7 +21,7 @@ impl VirtualDom {
let entry = self.scopes.vacant_entry(); let entry = self.scopes.vacant_entry();
let id = ScopeId(entry.key()); let id = ScopeId(entry.key());
entry.insert(ScopeState { let scope = entry.insert(ScopeState {
runtime: self.runtime.clone(), runtime: self.runtime.clone(),
context_id: id, context_id: id,
@ -35,7 +36,13 @@ impl VirtualDom {
borrowed_props: Default::default(), borrowed_props: Default::default(),
attributes_to_drop: Default::default(), attributes_to_drop: Default::default(),
}) });
let context =
ScopeContext::new(name, id, parent_id, height, self.runtime.scheduler.clone());
self.runtime.create_context_at(id, context);
scope
} }
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn { pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {

View file

@ -32,12 +32,23 @@ pub struct ScopeContext {
} }
impl ScopeContext { impl ScopeContext {
pub fn name(&self) -> &str { pub(crate) fn new(
self.name name: &'static str,
} id: ScopeId,
parent_id: Option<ScopeId>,
pub fn height(&self) -> u32 { height: u32,
self.height tasks: Rc<Scheduler>,
) -> Self {
Self {
name,
id,
parent_id,
height,
suspended: Cell::new(false),
shared_contexts: RefCell::new(vec![]),
tasks,
spawned_tasks: RefCell::new(FxHashSet::default()),
}
} }
pub fn parent_id(&self) -> Option<ScopeId> { pub fn parent_id(&self) -> Option<ScopeId> {

View file

@ -13,7 +13,7 @@ use crate::{
use bumpalo::{boxed::Box as BumpBox, Bump}; use bumpalo::{boxed::Box as BumpBox, Bump};
use std::{ use std::{
any::Any, any::Any,
cell::{Cell, RefCell, UnsafeCell}, cell::{Cell, Ref, RefCell, UnsafeCell},
fmt::{Arguments, Debug}, fmt::{Arguments, Debug},
future::Future, future::Future,
rc::Rc, rc::Rc,
@ -84,8 +84,14 @@ pub struct ScopeState {
pub(crate) props: Option<Box<dyn AnyProps<'static>>>, pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
} }
impl Drop for ScopeState {
fn drop(&mut self) {
self.runtime.remove_context(self.context_id);
}
}
impl<'src> ScopeState { impl<'src> ScopeState {
pub(crate) fn context(&self) -> &ScopeContext { pub(crate) fn context(&self) -> Ref<'_, ScopeContext> {
self.runtime.get_context(self.context_id).unwrap() self.runtime.get_context(self.context_id).unwrap()
} }
@ -494,7 +500,9 @@ impl<'src> ScopeState {
/// Mark this component as suspended and then return None /// Mark this component as suspended and then return None
pub fn suspend(&self) -> Option<Element> { pub fn suspend(&self) -> Option<Element> {
self.context().suspend() let cx = self.context();
cx.suspend();
None
} }
/// Store a value between renders. The foundational hook for all other hooks. /// Store a value between renders. The foundational hook for all other hooks.