1#![deny(missing_docs)]
24
25use crate::context::{StyleContext, ThreadLocalStyleContext};
26use crate::dom::{OpaqueNode, SendNode, TElement};
27use crate::scoped_tls::ScopedTLS;
28use crate::traversal::DomTraversal;
29use std::collections::VecDeque;
30
31#[cfg(feature = "gecko")]
33pub const STYLE_THREAD_STACK_SIZE_KB: usize = 256;
34
35#[cfg(feature = "servo")]
40pub const STYLE_THREAD_STACK_SIZE_KB: usize = const {
41 let default_stack_size = 512;
42 if let Some(user_def_size) = option_env!("SERVO_STYLE_THREAD_STACK_SIZE_KB") {
43 if let Ok(user_def_size) = usize::from_str_radix(user_def_size, 10) {
44 user_def_size
45 } else {
46 panic!("SERVO_STYLE_THREAD_STACK_SIZE_KB must be a valid integer")
47 }
48 } else {
49 default_stack_size
50 }
51};
52
53pub const STACK_SAFETY_MARGIN_KB: usize = 168;
70
71#[inline(never)]
75pub(crate) fn create_thread_local_context<'scope, E>(slot: &mut Option<ThreadLocalStyleContext<E>>)
76where
77 E: TElement + 'scope,
78{
79 *slot = Some(ThreadLocalStyleContext::new());
80}
81
82fn distribute_one_chunk<'a, 'scope, E, D>(
84 items: VecDeque<SendNode<E::ConcreteNode>>,
85 traversal_root: OpaqueNode,
86 work_unit_max: usize,
87 dom_depth: usize,
88 scope: &'a rayon::ScopeFifo<'scope>,
89 traversal: &'scope D,
90 tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>,
91) where
92 E: TElement + 'scope,
93 D: DomTraversal<E>,
94{
95 scope.spawn_fifo(move |scope| {
96 #[cfg(feature = "gecko")]
97 gecko_profiler_label!(Layout, StyleComputation);
98 let mut tlc = tls.ensure(create_thread_local_context);
99 tlc.current_dom_depth = dom_depth;
100 let mut context = StyleContext {
101 shared: traversal.shared_context(),
102 thread_local: &mut *tlc,
103 };
104 style_trees(
105 &mut context,
106 items,
107 traversal_root,
108 work_unit_max,
109 Some(scope),
110 traversal,
111 tls,
112 );
113 })
114}
115
116fn distribute_work<'a, 'scope, E, D>(
118 mut items: impl Iterator<Item = SendNode<E::ConcreteNode>>,
119 traversal_root: OpaqueNode,
120 work_unit_max: usize,
121 dom_depth: usize,
122 scope: &'a rayon::ScopeFifo<'scope>,
123 traversal: &'scope D,
124 tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>,
125) where
126 E: TElement + 'scope,
127 D: DomTraversal<E>,
128{
129 use std::iter::FromIterator;
130 loop {
131 let chunk = VecDeque::from_iter(items.by_ref().take(work_unit_max));
132 if chunk.is_empty() {
133 return;
134 }
135 distribute_one_chunk(
136 chunk,
137 traversal_root,
138 work_unit_max,
139 dom_depth,
140 scope,
141 traversal,
142 tls,
143 );
144 }
145}
146
147#[inline]
149pub fn style_trees<'a, 'scope, E, D>(
150 context: &mut StyleContext<E>,
151 mut discovered: VecDeque<SendNode<E::ConcreteNode>>,
152 traversal_root: OpaqueNode,
153 work_unit_max: usize,
154 scope: Option<&'a rayon::ScopeFifo<'scope>>,
155 traversal: &'scope D,
156 tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>,
157) where
158 E: TElement + 'scope,
159 D: DomTraversal<E>,
160{
161 let local_queue_size = if tls.current_thread_index() == 0 {
162 crate::pref!("layout.css.stylo-local-work-queue.in-main-thread")
163 } else {
164 crate::pref!("layout.css.stylo-local-work-queue.in-worker")
165 } as usize;
166
167 let mut nodes_remaining_at_current_depth = discovered.len();
168 while let Some(node) = discovered.pop_front() {
169 let mut children_to_process = 0isize;
170 traversal.process_preorder(context, *node, |n| {
171 children_to_process += 1;
172 discovered.push_back(unsafe { SendNode::new(n) });
173 });
174
175 traversal.handle_postorder_traversal(context, traversal_root, *node, children_to_process);
176
177 nodes_remaining_at_current_depth -= 1;
178
179 let discovered_children = discovered.len() - nodes_remaining_at_current_depth;
183 if discovered_children >= work_unit_max
184 && discovered.len() >= local_queue_size + work_unit_max
185 && scope.is_some()
186 {
187 let kept_work = std::cmp::max(nodes_remaining_at_current_depth, local_queue_size);
188 distribute_work(
189 discovered.range(kept_work..).cloned(),
190 traversal_root,
191 work_unit_max,
192 context.thread_local.current_dom_depth + 1,
193 scope.unwrap(),
194 traversal,
195 tls,
196 );
197 discovered.truncate(kept_work);
198 }
199
200 if nodes_remaining_at_current_depth == 0 {
201 context.thread_local.current_dom_depth += 1;
202 nodes_remaining_at_current_depth = discovered.len();
203 }
204 }
205}