1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#![deny(missing_docs)]
use context::{StyleContext, ThreadLocalStyleContext, TraversalStatistics};
use dom::{SendNode, TElement, TNode};
use parallel;
use parallel::{DispatchMode, WORK_UNIT_MAX};
use rayon;
use scoped_tls::ScopedTLS;
use std::collections::VecDeque;
use std::mem;
use time;
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
pub fn traverse_dom<E, D>(
traversal: &D,
token: PreTraverseToken<E>,
pool: Option<&rayon::ThreadPool>
) -> (bool, Option<TraversalStatistics>)
where
E: TElement,
D: DomTraversal<E>,
{
let root =
token.traversal_root().expect("Should've ensured we needed to traverse");
let dump_stats = traversal.shared_context().options.dump_style_statistics;
let is_nightly = traversal.shared_context().options.is_nightly();
let mut used_parallel = false;
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
let mut maybe_tls: Option<ScopedTLS<ThreadLocalStyleContext<E>>> = None;
let mut tlc = ThreadLocalStyleContext::new(traversal.shared_context());
let mut context = StyleContext {
shared: traversal.shared_context(),
thread_local: &mut tlc,
};
let mut discovered =
VecDeque::<SendNode<E::ConcreteNode>>::with_capacity(WORK_UNIT_MAX * 2);
let mut depth = root.depth();
let mut nodes_remaining_at_current_depth = 1;
discovered.push_back(unsafe { SendNode::new(root.as_node()) });
while let Some(node) = discovered.pop_front() {
let mut children_to_process = 0isize;
let traversal_data = PerLevelTraversalData { current_dom_depth: depth };
traversal.process_preorder(&traversal_data, &mut context, *node, |n| {
children_to_process += 1;
discovered.push_back(unsafe { SendNode::new(n) });
});
traversal.handle_postorder_traversal(&mut context, root.as_node().opaque(),
*node, children_to_process);
nodes_remaining_at_current_depth -= 1;
if nodes_remaining_at_current_depth == 0 {
depth += 1;
if pool.is_some() && discovered.len() > WORK_UNIT_MAX {
used_parallel = true;
let pool = pool.unwrap();
maybe_tls = Some(ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool));
let root_opaque = root.as_node().opaque();
let drain = discovered.drain(..);
pool.install(|| {
rayon::scope(|scope| {
parallel::traverse_nodes(
drain,
DispatchMode::TailCall,
true,
root_opaque,
PerLevelTraversalData { current_dom_depth: depth },
scope,
pool,
traversal,
maybe_tls.as_ref().unwrap()
);
});
});
break;
}
nodes_remaining_at_current_depth = discovered.len();
}
}
let mut maybe_stats = None;
if dump_stats || is_nightly {
let mut aggregate =
mem::replace(&mut context.thread_local.statistics, Default::default());
let parallel = maybe_tls.is_some();
if let Some(ref mut tls) = maybe_tls {
let slots = unsafe { tls.unsafe_get() };
aggregate = slots.iter().fold(aggregate, |acc, t| {
match *t.borrow() {
None => acc,
Some(ref cx) => &cx.statistics + &acc,
}
});
}
if dump_stats && aggregate.is_large_traversal() {
aggregate.finish(traversal, parallel, start_time.unwrap());
println!("{}", aggregate);
}
maybe_stats = Some(aggregate);
}
(used_parallel, maybe_stats)
}