Skip to content

Commit e832c84

Browse files
authored
yew: reduce scheduler call indirection (#1903)
1 parent 5eda7ed commit e832c84

File tree

4 files changed

+103
-124
lines changed

4 files changed

+103
-124
lines changed

packages/yew/src/agent/link.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::*;
22
use crate::callback::Callback;
3-
use crate::scheduler::{scheduler, Runnable, Shared};
3+
use crate::scheduler::{self, Runnable, Shared};
44
use std::cell::RefCell;
55
use std::fmt;
66
use std::rc::Rc;
@@ -109,11 +109,10 @@ impl<AGN: Agent> AgentScope<AGN> {
109109

110110
/// Schedule message for sending to agent
111111
pub fn send(&self, event: AgentLifecycleEvent<AGN>) {
112-
let runnable: Box<dyn Runnable> = Box::new(AgentRunnable {
112+
scheduler::push(Box::new(AgentRunnable {
113113
state: self.state.clone(),
114114
event,
115-
});
116-
scheduler().push(runnable);
115+
}));
117116
}
118117
}
119118

packages/yew/src/html/component/lifecycle.rs

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Component lifecycle module
22
33
use super::{Component, Scope};
4-
use crate::scheduler::{scheduler, ComponentRunnableType, Runnable, Shared};
4+
use crate::scheduler::{self, Runnable, Shared};
55
use crate::virtual_dom::{VDiff, VNode};
66
use crate::NodeRef;
77
use web_sys::Element;
@@ -44,14 +44,12 @@ impl<COMP: Component> ComponentState<COMP> {
4444

4545
fn drain_pending_updates(&mut self, state: &Shared<Option<ComponentState<COMP>>>) {
4646
if !self.pending_updates.is_empty() {
47-
scheduler()
48-
.component
49-
.push_update_batch(self.pending_updates.drain(..).map(|update| {
50-
Box::new(ComponentRunnable {
51-
state: state.clone(),
52-
event: update.into(),
53-
}) as Box<dyn Runnable>
54-
}));
47+
scheduler::push_component_updates(self.pending_updates.drain(..).map(|update| {
48+
Box::new(ComponentRunnable {
49+
state: state.clone(),
50+
event: update.into(),
51+
}) as Box<dyn Runnable>
52+
}));
5553
}
5654
}
5755
}
@@ -65,18 +63,6 @@ pub(crate) enum ComponentLifecycleEvent<COMP: Component> {
6563
Destroy,
6664
}
6765

68-
impl<COMP: Component> ComponentLifecycleEvent<COMP> {
69-
pub(crate) fn as_runnable_type(&self) -> ComponentRunnableType {
70-
match self {
71-
Self::Create(_) => ComponentRunnableType::Create,
72-
Self::Update(_) => ComponentRunnableType::Update,
73-
Self::Render => ComponentRunnableType::Render,
74-
Self::Rendered => ComponentRunnableType::Rendered,
75-
Self::Destroy => ComponentRunnableType::Destroy,
76-
}
77-
}
78-
}
79-
8066
impl<COMP: Component> From<CreateEvent<COMP>> for ComponentLifecycleEvent<COMP> {
8167
fn from(create: CreateEvent<COMP>) -> Self {
8268
Self::Create(create)

packages/yew/src/html/component/scope.rs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use super::{
99
use crate::callback::Callback;
1010
use crate::context::{ContextHandle, ContextProvider};
1111
use crate::html::NodeRef;
12-
use crate::scheduler::{scheduler, Shared};
12+
use crate::scheduler::{self, Shared};
1313
use crate::utils::document;
1414
use crate::virtual_dom::{insert_node, VNode};
1515
use std::any::{Any, TypeId};
@@ -191,26 +191,24 @@ impl<COMP: Component> Scope<COMP> {
191191
}
192192

193193
pub(crate) fn process(&self, event: ComponentLifecycleEvent<COMP>) {
194-
let scheduler = scheduler();
195-
scheduler.component.push(
196-
event.as_runnable_type(),
197-
Box::new(ComponentRunnable {
198-
state: self.state.clone(),
199-
event,
200-
}),
201-
);
202-
scheduler.start();
194+
self.schedule(event);
195+
scheduler::start();
203196
}
204197

205198
fn schedule(&self, event: ComponentLifecycleEvent<COMP>) {
206-
let scheduler = &scheduler().component;
207-
scheduler.push(
208-
event.as_runnable_type(),
209-
Box::new(ComponentRunnable {
210-
state: self.state.clone(),
211-
event,
212-
}),
213-
);
199+
use ComponentLifecycleEvent::*;
200+
201+
let push = match &event {
202+
Create(_) => scheduler::push_component_create,
203+
Update(_) => scheduler::push_component_update,
204+
Render => scheduler::push_component_render,
205+
Rendered => scheduler::push_component_rendered,
206+
Destroy => scheduler::push_component_destroy,
207+
};
208+
push(Box::new(ComponentRunnable {
209+
state: self.state.clone(),
210+
event,
211+
}));
214212
}
215213

216214
/// Send a message to the component.

packages/yew/src/scheduler.rs

Lines changed: 77 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ use std::rc::Rc;
77
pub(crate) type Shared<T> = Rc<RefCell<T>>;
88

99
thread_local! {
10-
static SCHEDULER: Rc<Scheduler> =
11-
Rc::new(Scheduler::new());
12-
}
13-
14-
pub(crate) fn scheduler() -> Rc<Scheduler> {
15-
SCHEDULER.with(Rc::clone)
10+
/// This is a global scheduler suitable to schedule and run any tasks.
11+
///
12+
/// Exclusivity of mutable access is controlled by only accessing it through a set of public
13+
/// functions.
14+
static SCHEDULER: RefCell<Scheduler> = Default::default();
1615
}
1716

1817
/// A routine which could be run.
@@ -22,98 +21,95 @@ pub(crate) trait Runnable {
2221
}
2322

2423
/// This is a global scheduler suitable to schedule and run any tasks.
25-
#[derive(Clone)]
26-
pub(crate) struct Scheduler {
27-
/// This lock is used to prevent recursion in [Scheduler#start()](Scheduler#start())
28-
lock: Rc<RefCell<()>>,
29-
main: Shared<VecDeque<Box<dyn Runnable>>>,
30-
pub(crate) component: ComponentScheduler,
31-
}
24+
#[derive(Default)]
25+
struct Scheduler {
26+
// Main queue
27+
main: VecDeque<Box<dyn Runnable>>,
28+
29+
// Component queues
30+
destroy: VecDeque<Box<dyn Runnable>>,
31+
create: VecDeque<Box<dyn Runnable>>,
32+
update: VecDeque<Box<dyn Runnable>>,
33+
render: VecDeque<Box<dyn Runnable>>,
3234

33-
pub(crate) enum ComponentRunnableType {
34-
Create,
35-
Update,
36-
Render,
37-
Rendered,
38-
Destroy,
35+
// Stack
36+
rendered: Vec<Box<dyn Runnable>>,
3937
}
4038

41-
#[derive(Clone)]
42-
pub(crate) struct ComponentScheduler {
43-
// Queues
44-
destroy: Shared<VecDeque<Box<dyn Runnable>>>,
45-
create: Shared<VecDeque<Box<dyn Runnable>>>,
46-
update: Shared<VecDeque<Box<dyn Runnable>>>,
47-
render: Shared<VecDeque<Box<dyn Runnable>>>,
39+
/// Execute closure with a mutable reference to the scheduler
40+
#[inline]
41+
fn with(f: impl FnOnce(&mut Scheduler)) {
42+
SCHEDULER.with(|s| f(&mut *s.borrow_mut()));
43+
}
4844

49-
// Stack
50-
rendered: Shared<Vec<Box<dyn Runnable>>>,
45+
/// Push a generic Runnable to be executed
46+
#[inline]
47+
pub(crate) fn push(runnable: Box<dyn Runnable>) {
48+
with(|s| s.main.push_back(runnable));
5149
}
5250

53-
impl ComponentScheduler {
54-
fn new() -> Self {
55-
ComponentScheduler {
56-
destroy: Rc::new(RefCell::new(VecDeque::new())),
57-
create: Rc::new(RefCell::new(VecDeque::new())),
58-
update: Rc::new(RefCell::new(VecDeque::new())),
59-
render: Rc::new(RefCell::new(VecDeque::new())),
60-
rendered: Rc::new(RefCell::new(Vec::new())),
61-
}
62-
}
51+
/// Push a component creation Runnable to be executed
52+
#[inline]
53+
pub(crate) fn push_component_create(runnable: Box<dyn Runnable>) {
54+
with(|s| s.create.push_back(runnable));
55+
}
6356

64-
pub(crate) fn push_update_batch(&self, it: impl IntoIterator<Item = Box<dyn Runnable>>) {
65-
self.update.borrow_mut().extend(it);
66-
}
57+
/// Push a component destruction Runnable to be executed
58+
#[inline]
59+
pub(crate) fn push_component_destroy(runnable: Box<dyn Runnable>) {
60+
with(|s| s.destroy.push_back(runnable));
61+
}
6762

68-
pub(crate) fn push(&self, run_type: ComponentRunnableType, runnable: Box<dyn Runnable>) {
69-
match run_type {
70-
ComponentRunnableType::Create => self.create.borrow_mut().push_back(runnable),
71-
ComponentRunnableType::Update => self.update.borrow_mut().push_back(runnable),
72-
ComponentRunnableType::Render => self.render.borrow_mut().push_back(runnable),
73-
ComponentRunnableType::Rendered => self.rendered.borrow_mut().push(runnable),
74-
ComponentRunnableType::Destroy => self.destroy.borrow_mut().push_back(runnable),
75-
};
76-
}
63+
/// Push a component render Runnable to be executed
64+
#[inline]
65+
pub(crate) fn push_component_render(runnable: Box<dyn Runnable>) {
66+
with(|s| s.render.push_back(runnable));
67+
}
7768

78-
fn next_runnable(&self) -> Option<Box<dyn Runnable>> {
79-
self.destroy
80-
.borrow_mut()
81-
.pop_front()
82-
.or_else(|| self.create.borrow_mut().pop_front())
83-
.or_else(|| self.update.borrow_mut().pop_front())
84-
.or_else(|| self.render.borrow_mut().pop_front())
85-
.or_else(|| self.rendered.borrow_mut().pop())
86-
}
69+
/// Push a component Runnable to be executed after a component is rendered
70+
#[inline]
71+
pub(crate) fn push_component_rendered(runnable: Box<dyn Runnable>) {
72+
with(|s| s.rendered.push(runnable));
8773
}
8874

89-
impl Scheduler {
90-
fn new() -> Self {
91-
Scheduler {
92-
lock: Rc::new(RefCell::new(())),
93-
main: Rc::new(RefCell::new(VecDeque::new())),
94-
component: ComponentScheduler::new(),
95-
}
96-
}
75+
/// Push a component update Runnable to be executed
76+
#[inline]
77+
pub(crate) fn push_component_update(runnable: Box<dyn Runnable>) {
78+
with(|s| s.update.push_back(runnable));
79+
}
9780

98-
pub(crate) fn push(&self, runnable: Box<dyn Runnable>) {
99-
self.main.borrow_mut().push_back(runnable);
100-
self.start();
101-
}
81+
/// Push a batch of component updates to be executed
82+
#[inline]
83+
pub(crate) fn push_component_updates(it: impl IntoIterator<Item = Box<dyn Runnable>>) {
84+
with(|s| s.update.extend(it));
85+
}
10286

103-
fn next_runnable(&self) -> Option<Box<dyn Runnable>> {
104-
self.component
105-
.next_runnable()
106-
.or_else(|| self.main.borrow_mut().pop_front())
87+
/// Execute any pending Runnables
88+
pub(crate) fn start() {
89+
thread_local! {
90+
// The lock is used to prevent recursion. If the lock cannot be acquired, it is because the
91+
// `start()` method is being called recursively as part of a `runnable.run()`.
92+
static LOCK: RefCell<()> = Default::default();
10793
}
10894

109-
pub(crate) fn start(&self) {
110-
// The lock is used to prevent recursion. If the lock
111-
// cannot be acquired, it is because the `start()` method
112-
// is being called recursively as part of a `runnable.run()`.
113-
if let Ok(_lock) = self.lock.try_borrow_mut() {
114-
while let Some(runnable) = self.next_runnable() {
95+
LOCK.with(|l| {
96+
if let Ok(_lock) = l.try_borrow_mut() {
97+
while let Some(runnable) = SCHEDULER.with(|s| s.borrow_mut().next_runnable()) {
11598
runnable.run();
11699
}
117100
}
101+
});
102+
}
103+
104+
impl Scheduler {
105+
/// Pop next Runnable to be executed according to Runnable type execution priority
106+
fn next_runnable(&mut self) -> Option<Box<dyn Runnable>> {
107+
self.destroy
108+
.pop_front()
109+
.or_else(|| self.create.pop_front())
110+
.or_else(|| self.update.pop_front())
111+
.or_else(|| self.render.pop_front())
112+
.or_else(|| self.rendered.pop())
113+
.or_else(|| self.main.pop_front())
118114
}
119115
}

0 commit comments

Comments
 (0)