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(all(unix, not(target_arch = "wasm32")))]
15use std::os::unix::thread::{JoinHandleExt, RawPthread};
16#[cfg(windows)]
17use std::os::windows::{io::AsRawHandle, prelude::RawHandle};
18use std::{io, sync::LazyLock, thread};
19use thin_vec::ThinVec;
20
21#[cfg(all(unix, not(target_arch = "wasm32")))]
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
68static STYLE_THREAD_JOIN_HANDLES: Mutex<Vec<thread::JoinHandle<()>>> = Mutex::new(Vec::new());
76
77fn thread_spawn(options: rayon::ThreadBuilder) -> io::Result<()> {
78 let mut b = thread::Builder::new();
79 if let Some(name) = options.name() {
80 b = b.name(name.to_owned());
81 }
82 if let Some(stack_size) = options.stack_size() {
83 b = b.stack_size(stack_size);
84 }
85 let join_handle = b.spawn(|| options.run())?;
86 STYLE_THREAD_JOIN_HANDLES.lock().push(join_handle);
87 Ok(())
88}
89
90fn thread_startup(_index: usize) {
91 thread_state::initialize_layout_worker_thread();
92 #[cfg(feature = "gecko")]
93 unsafe {
94 bindings::Gecko_SetJemallocThreadLocalArena(true);
95 let name = thread_name(_index);
96 gecko_profiler::register_thread(&name);
97 }
98}
99
100fn thread_shutdown(_: usize) {
101 #[cfg(feature = "gecko")]
102 unsafe {
103 gecko_profiler::unregister_thread();
104 bindings::Gecko_SetJemallocThreadLocalArena(false);
105 }
106}
107
108impl StyleThreadPool {
109 pub fn shutdown() {
111 if STYLE_THREAD_JOIN_HANDLES.lock().is_empty() {
112 return;
113 }
114 {
115 let _ = STYLE_THREAD_POOL.style_thread_pool.write().take();
117 }
118
119 while let Some(join_handle) = STYLE_THREAD_JOIN_HANDLES.lock().pop() {
123 let _ = join_handle.join();
124 }
125 }
126
127 pub fn pool(&self) -> RwLockReadGuard<'_, Option<rayon::ThreadPool>> {
132 self.style_thread_pool.read()
133 }
134
135 pub fn get_thread_handles(handles: &mut ThinVec<PlatformThreadHandle>) {
137 LazyLock::force(&STYLE_THREAD_POOL);
140
141 for join_handle in STYLE_THREAD_JOIN_HANDLES.lock().iter() {
142 #[cfg(all(unix, not(target_arch = "wasm32")))]
143 let handle = join_handle.as_pthread_t();
144 #[cfg(windows)]
145 let handle = join_handle.as_raw_handle();
146 #[cfg(all(target_arch = "wasm32", not(feature = "gecko")))]
147 let handle = {
148 let _ = join_handle;
149 DummyThreadHandle
150 };
151
152 handles.push(handle);
153 }
154 }
155}
156
157#[cfg(feature = "servo")]
158fn stylo_threads_pref() -> i32 {
159 style_config::get_i32("layout.threads")
160}
161
162#[cfg(feature = "gecko")]
163fn stylo_threads_pref() -> i32 {
164 static_prefs::pref!("layout.css.stylo-threads")
165}
166
167pub(crate) const STYLO_MAX_THREADS: usize = 6;
170
171pub static STYLE_THREAD_POOL: LazyLock<StyleThreadPool> = LazyLock::new(|| {
173 use std::cmp;
174 let threads_pref: i32 = stylo_threads_pref();
177 let num_threads = if threads_pref >= 0 {
178 threads_pref as usize
179 } else {
180 #[cfg(feature = "gecko")]
183 let num_threads = unsafe { bindings::Gecko_GetNumStyleThreads() };
184 #[cfg(not(feature = "gecko"))]
185 let num_threads = -1;
186
187 if num_threads >= 0 {
188 num_threads as usize
189 } else {
190 cmp::max(num_cpus::get() * 3 / 4, 1)
193 }
194 };
195
196 let num_threads = cmp::min(num_threads, STYLO_MAX_THREADS);
197 let (pool, num_threads) = if num_threads <= 1 {
200 (None, None)
201 } else {
202 let workers = rayon::ThreadPoolBuilder::new()
203 .spawn_handler(thread_spawn)
204 .use_current_thread()
205 .num_threads(num_threads)
206 .thread_name(thread_name)
207 .start_handler(thread_startup)
208 .exit_handler(thread_shutdown)
209 .stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024)
210 .build();
211 (workers.ok(), Some(num_threads))
212 };
213
214 StyleThreadPool {
215 num_threads,
216 style_thread_pool: RwLock::new(pool),
217 }
218});
219
220pub static GLOBAL_STYLE_DATA: LazyLock<GlobalStyleData> = LazyLock::new(|| GlobalStyleData {
222 shared_lock: SharedRwLock::new_leaked(),
223 options: StyleSystemOptions::default(),
224});