mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
yew: reduce scheduler call indirection (#1903)
This commit is contained in:
parent
5eda7ed5dc
commit
e832c84623
@ -1,6 +1,6 @@
|
||||
use super::*;
|
||||
use crate::callback::Callback;
|
||||
use crate::scheduler::{scheduler, Runnable, Shared};
|
||||
use crate::scheduler::{self, Runnable, Shared};
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
@ -109,11 +109,10 @@ impl<AGN: Agent> AgentScope<AGN> {
|
||||
|
||||
/// Schedule message for sending to agent
|
||||
pub fn send(&self, event: AgentLifecycleEvent<AGN>) {
|
||||
let runnable: Box<dyn Runnable> = Box::new(AgentRunnable {
|
||||
scheduler::push(Box::new(AgentRunnable {
|
||||
state: self.state.clone(),
|
||||
event,
|
||||
});
|
||||
scheduler().push(runnable);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! Component lifecycle module
|
||||
|
||||
use super::{Component, Scope};
|
||||
use crate::scheduler::{scheduler, ComponentRunnableType, Runnable, Shared};
|
||||
use crate::scheduler::{self, Runnable, Shared};
|
||||
use crate::virtual_dom::{VDiff, VNode};
|
||||
use crate::NodeRef;
|
||||
use web_sys::Element;
|
||||
@ -44,14 +44,12 @@ impl<COMP: Component> ComponentState<COMP> {
|
||||
|
||||
fn drain_pending_updates(&mut self, state: &Shared<Option<ComponentState<COMP>>>) {
|
||||
if !self.pending_updates.is_empty() {
|
||||
scheduler()
|
||||
.component
|
||||
.push_update_batch(self.pending_updates.drain(..).map(|update| {
|
||||
Box::new(ComponentRunnable {
|
||||
state: state.clone(),
|
||||
event: update.into(),
|
||||
}) as Box<dyn Runnable>
|
||||
}));
|
||||
scheduler::push_component_updates(self.pending_updates.drain(..).map(|update| {
|
||||
Box::new(ComponentRunnable {
|
||||
state: state.clone(),
|
||||
event: update.into(),
|
||||
}) as Box<dyn Runnable>
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,18 +63,6 @@ pub(crate) enum ComponentLifecycleEvent<COMP: Component> {
|
||||
Destroy,
|
||||
}
|
||||
|
||||
impl<COMP: Component> ComponentLifecycleEvent<COMP> {
|
||||
pub(crate) fn as_runnable_type(&self) -> ComponentRunnableType {
|
||||
match self {
|
||||
Self::Create(_) => ComponentRunnableType::Create,
|
||||
Self::Update(_) => ComponentRunnableType::Update,
|
||||
Self::Render => ComponentRunnableType::Render,
|
||||
Self::Rendered => ComponentRunnableType::Rendered,
|
||||
Self::Destroy => ComponentRunnableType::Destroy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<COMP: Component> From<CreateEvent<COMP>> for ComponentLifecycleEvent<COMP> {
|
||||
fn from(create: CreateEvent<COMP>) -> Self {
|
||||
Self::Create(create)
|
||||
|
||||
@ -9,7 +9,7 @@ use super::{
|
||||
use crate::callback::Callback;
|
||||
use crate::context::{ContextHandle, ContextProvider};
|
||||
use crate::html::NodeRef;
|
||||
use crate::scheduler::{scheduler, Shared};
|
||||
use crate::scheduler::{self, Shared};
|
||||
use crate::utils::document;
|
||||
use crate::virtual_dom::{insert_node, VNode};
|
||||
use std::any::{Any, TypeId};
|
||||
@ -191,26 +191,24 @@ impl<COMP: Component> Scope<COMP> {
|
||||
}
|
||||
|
||||
pub(crate) fn process(&self, event: ComponentLifecycleEvent<COMP>) {
|
||||
let scheduler = scheduler();
|
||||
scheduler.component.push(
|
||||
event.as_runnable_type(),
|
||||
Box::new(ComponentRunnable {
|
||||
state: self.state.clone(),
|
||||
event,
|
||||
}),
|
||||
);
|
||||
scheduler.start();
|
||||
self.schedule(event);
|
||||
scheduler::start();
|
||||
}
|
||||
|
||||
fn schedule(&self, event: ComponentLifecycleEvent<COMP>) {
|
||||
let scheduler = &scheduler().component;
|
||||
scheduler.push(
|
||||
event.as_runnable_type(),
|
||||
Box::new(ComponentRunnable {
|
||||
state: self.state.clone(),
|
||||
event,
|
||||
}),
|
||||
);
|
||||
use ComponentLifecycleEvent::*;
|
||||
|
||||
let push = match &event {
|
||||
Create(_) => scheduler::push_component_create,
|
||||
Update(_) => scheduler::push_component_update,
|
||||
Render => scheduler::push_component_render,
|
||||
Rendered => scheduler::push_component_rendered,
|
||||
Destroy => scheduler::push_component_destroy,
|
||||
};
|
||||
push(Box::new(ComponentRunnable {
|
||||
state: self.state.clone(),
|
||||
event,
|
||||
}));
|
||||
}
|
||||
|
||||
/// Send a message to the component.
|
||||
|
||||
@ -7,12 +7,11 @@ use std::rc::Rc;
|
||||
pub(crate) type Shared<T> = Rc<RefCell<T>>;
|
||||
|
||||
thread_local! {
|
||||
static SCHEDULER: Rc<Scheduler> =
|
||||
Rc::new(Scheduler::new());
|
||||
}
|
||||
|
||||
pub(crate) fn scheduler() -> Rc<Scheduler> {
|
||||
SCHEDULER.with(Rc::clone)
|
||||
/// This is a global scheduler suitable to schedule and run any tasks.
|
||||
///
|
||||
/// Exclusivity of mutable access is controlled by only accessing it through a set of public
|
||||
/// functions.
|
||||
static SCHEDULER: RefCell<Scheduler> = Default::default();
|
||||
}
|
||||
|
||||
/// A routine which could be run.
|
||||
@ -22,98 +21,95 @@ pub(crate) trait Runnable {
|
||||
}
|
||||
|
||||
/// This is a global scheduler suitable to schedule and run any tasks.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Scheduler {
|
||||
/// This lock is used to prevent recursion in [Scheduler#start()](Scheduler#start())
|
||||
lock: Rc<RefCell<()>>,
|
||||
main: Shared<VecDeque<Box<dyn Runnable>>>,
|
||||
pub(crate) component: ComponentScheduler,
|
||||
}
|
||||
#[derive(Default)]
|
||||
struct Scheduler {
|
||||
// Main queue
|
||||
main: VecDeque<Box<dyn Runnable>>,
|
||||
|
||||
pub(crate) enum ComponentRunnableType {
|
||||
Create,
|
||||
Update,
|
||||
Render,
|
||||
Rendered,
|
||||
Destroy,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ComponentScheduler {
|
||||
// Queues
|
||||
destroy: Shared<VecDeque<Box<dyn Runnable>>>,
|
||||
create: Shared<VecDeque<Box<dyn Runnable>>>,
|
||||
update: Shared<VecDeque<Box<dyn Runnable>>>,
|
||||
render: Shared<VecDeque<Box<dyn Runnable>>>,
|
||||
// Component queues
|
||||
destroy: VecDeque<Box<dyn Runnable>>,
|
||||
create: VecDeque<Box<dyn Runnable>>,
|
||||
update: VecDeque<Box<dyn Runnable>>,
|
||||
render: VecDeque<Box<dyn Runnable>>,
|
||||
|
||||
// Stack
|
||||
rendered: Shared<Vec<Box<dyn Runnable>>>,
|
||||
rendered: Vec<Box<dyn Runnable>>,
|
||||
}
|
||||
|
||||
impl ComponentScheduler {
|
||||
fn new() -> Self {
|
||||
ComponentScheduler {
|
||||
destroy: Rc::new(RefCell::new(VecDeque::new())),
|
||||
create: Rc::new(RefCell::new(VecDeque::new())),
|
||||
update: Rc::new(RefCell::new(VecDeque::new())),
|
||||
render: Rc::new(RefCell::new(VecDeque::new())),
|
||||
rendered: Rc::new(RefCell::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn push_update_batch(&self, it: impl IntoIterator<Item = Box<dyn Runnable>>) {
|
||||
self.update.borrow_mut().extend(it);
|
||||
}
|
||||
|
||||
pub(crate) fn push(&self, run_type: ComponentRunnableType, runnable: Box<dyn Runnable>) {
|
||||
match run_type {
|
||||
ComponentRunnableType::Create => self.create.borrow_mut().push_back(runnable),
|
||||
ComponentRunnableType::Update => self.update.borrow_mut().push_back(runnable),
|
||||
ComponentRunnableType::Render => self.render.borrow_mut().push_back(runnable),
|
||||
ComponentRunnableType::Rendered => self.rendered.borrow_mut().push(runnable),
|
||||
ComponentRunnableType::Destroy => self.destroy.borrow_mut().push_back(runnable),
|
||||
};
|
||||
}
|
||||
|
||||
fn next_runnable(&self) -> Option<Box<dyn Runnable>> {
|
||||
self.destroy
|
||||
.borrow_mut()
|
||||
.pop_front()
|
||||
.or_else(|| self.create.borrow_mut().pop_front())
|
||||
.or_else(|| self.update.borrow_mut().pop_front())
|
||||
.or_else(|| self.render.borrow_mut().pop_front())
|
||||
.or_else(|| self.rendered.borrow_mut().pop())
|
||||
}
|
||||
/// Execute closure with a mutable reference to the scheduler
|
||||
#[inline]
|
||||
fn with(f: impl FnOnce(&mut Scheduler)) {
|
||||
SCHEDULER.with(|s| f(&mut *s.borrow_mut()));
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
fn new() -> Self {
|
||||
Scheduler {
|
||||
lock: Rc::new(RefCell::new(())),
|
||||
main: Rc::new(RefCell::new(VecDeque::new())),
|
||||
component: ComponentScheduler::new(),
|
||||
}
|
||||
/// Push a generic Runnable to be executed
|
||||
#[inline]
|
||||
pub(crate) fn push(runnable: Box<dyn Runnable>) {
|
||||
with(|s| s.main.push_back(runnable));
|
||||
}
|
||||
|
||||
/// Push a component creation Runnable to be executed
|
||||
#[inline]
|
||||
pub(crate) fn push_component_create(runnable: Box<dyn Runnable>) {
|
||||
with(|s| s.create.push_back(runnable));
|
||||
}
|
||||
|
||||
/// Push a component destruction Runnable to be executed
|
||||
#[inline]
|
||||
pub(crate) fn push_component_destroy(runnable: Box<dyn Runnable>) {
|
||||
with(|s| s.destroy.push_back(runnable));
|
||||
}
|
||||
|
||||
/// Push a component render Runnable to be executed
|
||||
#[inline]
|
||||
pub(crate) fn push_component_render(runnable: Box<dyn Runnable>) {
|
||||
with(|s| s.render.push_back(runnable));
|
||||
}
|
||||
|
||||
/// Push a component Runnable to be executed after a component is rendered
|
||||
#[inline]
|
||||
pub(crate) fn push_component_rendered(runnable: Box<dyn Runnable>) {
|
||||
with(|s| s.rendered.push(runnable));
|
||||
}
|
||||
|
||||
/// Push a component update Runnable to be executed
|
||||
#[inline]
|
||||
pub(crate) fn push_component_update(runnable: Box<dyn Runnable>) {
|
||||
with(|s| s.update.push_back(runnable));
|
||||
}
|
||||
|
||||
/// Push a batch of component updates to be executed
|
||||
#[inline]
|
||||
pub(crate) fn push_component_updates(it: impl IntoIterator<Item = Box<dyn Runnable>>) {
|
||||
with(|s| s.update.extend(it));
|
||||
}
|
||||
|
||||
/// Execute any pending Runnables
|
||||
pub(crate) fn start() {
|
||||
thread_local! {
|
||||
// The lock is used to prevent recursion. If the lock cannot be acquired, it is because the
|
||||
// `start()` method is being called recursively as part of a `runnable.run()`.
|
||||
static LOCK: RefCell<()> = Default::default();
|
||||
}
|
||||
|
||||
pub(crate) fn push(&self, runnable: Box<dyn Runnable>) {
|
||||
self.main.borrow_mut().push_back(runnable);
|
||||
self.start();
|
||||
}
|
||||
|
||||
fn next_runnable(&self) -> Option<Box<dyn Runnable>> {
|
||||
self.component
|
||||
.next_runnable()
|
||||
.or_else(|| self.main.borrow_mut().pop_front())
|
||||
}
|
||||
|
||||
pub(crate) fn start(&self) {
|
||||
// The lock is used to prevent recursion. If the lock
|
||||
// cannot be acquired, it is because the `start()` method
|
||||
// is being called recursively as part of a `runnable.run()`.
|
||||
if let Ok(_lock) = self.lock.try_borrow_mut() {
|
||||
while let Some(runnable) = self.next_runnable() {
|
||||
LOCK.with(|l| {
|
||||
if let Ok(_lock) = l.try_borrow_mut() {
|
||||
while let Some(runnable) = SCHEDULER.with(|s| s.borrow_mut().next_runnable()) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
/// Pop next Runnable to be executed according to Runnable type execution priority
|
||||
fn next_runnable(&mut self) -> Option<Box<dyn Runnable>> {
|
||||
self.destroy
|
||||
.pop_front()
|
||||
.or_else(|| self.create.pop_front())
|
||||
.or_else(|| self.update.pop_front())
|
||||
.or_else(|| self.render.pop_front())
|
||||
.or_else(|| self.rendered.pop())
|
||||
.or_else(|| self.main.pop_front())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user