style/
global_style_data.rs1use crate::context::StyleSystemOptions;
8#[cfg(feature = "gecko")]
9use crate::gecko_bindings::bindings;
10use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
11use crate::shared_lock::SharedRwLock;
12use crate::thread_state;
13use parking_lot::{Mutex, RwLock, RwLockReadGuard};
14#[cfg(unix)]
15use std::os::unix::thread::{JoinHandleExt, RawPthread};
16#[cfg(windows)]
17use std::os::windows::{io::AsRawHandle, prelude::RawHandle};
18use std::{io, thread};
19use thin_vec::ThinVec;
20
21#[cfg(unix)]
23pub type PlatformThreadHandle = RawPthread;
24#[cfg(windows)]
26pub type PlatformThreadHandle = RawHandle;
27
28#[cfg(all(target_arch = "wasm32", not(feature = "gecko")))]
31pub struct DummyThreadHandle;
32#[cfg(all(target_arch = "wasm32", not(feature = "gecko")))]
33impl DummyThreadHandle {
34 pub fn join(&self) {
36 }
38}
39#[cfg(all(target_arch = "wasm32", not(feature = "gecko")))]
40pub type PlatformThreadHandle = DummyThreadHandle;
42
43pub struct GlobalStyleData {
45 pub shared_lock: SharedRwLock,
47
48 pub options: StyleSystemOptions,
50}
51
52pub struct StyleThreadPool {
54 pub num_threads: Option<usize>,
56
57 style_thread_pool: RwLock<Option<rayon::ThreadPool>>,
62}
63
64fn thread_name(index: usize) -> String {
65 format!("StyleThread#{}", index)
66}
67
68lazy_static! {
69 static ref STYLE_THREAD_JOIN_HANDLES: Mutex<Vec<thread::JoinHandle<()>>> =
77 Mutex::new(Vec::new());
78}
79
80fn thread_spawn(options: rayon::ThreadBuilder) -> io::Result<()> {
81 let mut b = thread::Builder::new();
82 if let Some(name) = options.name() {
83 b = b.name(name.to_owned());
84 }
85 if let Some(stack_size) = options.stack_size() {
86 b = b.stack_size(stack_size);
87 }
88 let join_handle = b.spawn(|| options.run())?;
89 STYLE_THREAD_JOIN_HANDLES.lock().push(join_handle);
90 Ok(())
91}
92
93fn thread_startup(_index: usize) {
94 thread_state::initialize_layout_worker_thread();
95 #[cfg(feature = "gecko")]
96 unsafe {
97 bindings::Gecko_SetJemallocThreadLocalArena(true);
98 let name = thread_name(_index);
99 gecko_profiler::register_thread(&name);
100 }
101}
102
103fn thread_shutdown(_: usize) {
104 #[cfg(feature = "gecko")]
105 unsafe {
106 gecko_profiler::unregister_thread();
107 bindings::Gecko_SetJemallocThreadLocalArena(false);
108 }
109}
110
111impl StyleThreadPool {
112 pub fn shutdown() {
114 if STYLE_THREAD_JOIN_HANDLES.lock().is_empty() {
115 return;
116 }
117 {
118 let _ = STYLE_THREAD_POOL.style_thread_pool.write().take();
120 }
121
122 while let Some(join_handle) = STYLE_THREAD_JOIN_HANDLES.lock().pop() {
126 let _ = join_handle.join();
127 }
128 }
129
130 pub fn pool(&self) -> RwLockReadGuard<Option<rayon::ThreadPool>> {
135 self.style_thread_pool.read()
136 }
137
138 pub fn get_thread_handles(handles: &mut ThinVec<PlatformThreadHandle>) {
140 lazy_static::initialize(&STYLE_THREAD_POOL);
143
144 for join_handle in STYLE_THREAD_JOIN_HANDLES.lock().iter() {
145 #[cfg(unix)]
146 let handle = join_handle.as_pthread_t();
147 #[cfg(windows)]
148 let handle = join_handle.as_raw_handle();
149 #[cfg(all(target_arch = "wasm32", not(feature = "gecko")))]
150 let handle = {
151 let _ = join_handle;
152 DummyThreadHandle
153 };
154
155 handles.push(handle);
156 }
157 }
158}
159
160#[cfg(feature = "servo")]
161fn stylo_threads_pref() -> i32 {
162 style_config::get_i32("layout.threads")
163}
164
165#[cfg(feature = "gecko")]
166fn stylo_threads_pref() -> i32 {
167 static_prefs::pref!("layout.css.stylo-threads")
168}
169
170pub(crate) const STYLO_MAX_THREADS: usize = 6;
173
174lazy_static! {
175 pub static ref STYLE_THREAD_POOL: StyleThreadPool = {
177 use std::cmp;
178 let threads_pref: i32 = stylo_threads_pref();
181 let num_threads = if threads_pref >= 0 {
182 threads_pref as usize
183 } else {
184 #[cfg(feature = "gecko")]
187 let num_threads = unsafe { bindings::Gecko_GetNumStyleThreads() };
188 #[cfg(not(feature = "gecko"))]
189 let num_threads = -1;
190
191 if num_threads >= 0 {
192 num_threads as usize
193 } else {
194 use num_cpus;
195 cmp::max(num_cpus::get() * 3 / 4, 1)
198 }
199 };
200
201 let num_threads = cmp::min(num_threads, STYLO_MAX_THREADS);
202 let (pool, num_threads) = if num_threads <= 1 {
205 (None, None)
206 } else {
207 let workers = rayon::ThreadPoolBuilder::new()
208 .spawn_handler(thread_spawn)
209 .use_current_thread()
210 .num_threads(num_threads)
211 .thread_name(thread_name)
212 .start_handler(thread_startup)
213 .exit_handler(thread_shutdown)
214 .stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024)
215 .build();
216 (workers.ok(), Some(num_threads))
217 };
218
219 StyleThreadPool {
220 num_threads,
221 style_thread_pool: RwLock::new(pool),
222 }
223 };
224
225 pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData {
227 shared_lock: SharedRwLock::new_leaked(),
228 options: StyleSystemOptions::default(),
229 };
230}