yew: reduce scheduler call indirection (#1903)

This commit is contained in:
bakape 2021-06-05 16:21:31 +03:00 committed by GitHub
parent 5eda7ed5dc
commit e832c84623
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 109 additions and 130 deletions

View File

@ -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);
}));
}
}

View File

@ -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)

View File

@ -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.

View File

@ -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())
}
}