From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- servo/components/style/global_style_data.rs | 212 ++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 servo/components/style/global_style_data.rs (limited to 'servo/components/style/global_style_data.rs') diff --git a/servo/components/style/global_style_data.rs b/servo/components/style/global_style_data.rs new file mode 100644 index 0000000000..38d72b2c74 --- /dev/null +++ b/servo/components/style/global_style_data.rs @@ -0,0 +1,212 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! Global style data + +use crate::context::StyleSystemOptions; +#[cfg(feature = "gecko")] +use crate::gecko_bindings::bindings; +use crate::parallel::STYLE_THREAD_STACK_SIZE_KB; +use crate::shared_lock::SharedRwLock; +use crate::thread_state; +use gecko_profiler; +use parking_lot::{Mutex, RwLock, RwLockReadGuard}; +use rayon; +#[cfg(unix)] +use std::os::unix::thread::{JoinHandleExt, RawPthread}; +#[cfg(windows)] +use std::os::windows::{io::AsRawHandle, prelude::RawHandle}; +use std::{io, thread}; +use thin_vec::ThinVec; + +/// Platform-specific handle to a thread. +#[cfg(unix)] +pub type PlatformThreadHandle = RawPthread; +/// Platform-specific handle to a thread. +#[cfg(windows)] +pub type PlatformThreadHandle = RawHandle; + +/// Global style data +pub struct GlobalStyleData { + /// Shared RWLock for CSSOM objects + pub shared_lock: SharedRwLock, + + /// Global style system options determined by env vars. + pub options: StyleSystemOptions, +} + +/// Global thread pool. +pub struct StyleThreadPool { + /// How many threads parallel styling can use. If not using a thread pool, this is set to `None`. + pub num_threads: Option, + + /// The parallel styling thread pool. + /// + /// For leak-checking purposes, we want to terminate the thread-pool, which + /// waits for all the async jobs to complete. Thus the RwLock. + style_thread_pool: RwLock>, +} + +fn thread_name(index: usize) -> String { + format!("StyleThread#{}", index) +} + +lazy_static! { + /// JoinHandles for spawned style threads. These will be joined during + /// StyleThreadPool::shutdown() after exiting the thread pool. + /// + /// This would be quite inefficient if rayon destroyed and re-created + /// threads regularly during threadpool operation in response to demand, + /// however rayon actually never destroys its threads until the entire + /// thread pool is shut-down, so the size of this list is bounded. + static ref STYLE_THREAD_JOIN_HANDLES: Mutex>> = + Mutex::new(Vec::new()); +} + +fn thread_spawn(options: rayon::ThreadBuilder) -> io::Result<()> { + let mut b = thread::Builder::new(); + if let Some(name) = options.name() { + b = b.name(name.to_owned()); + } + if let Some(stack_size) = options.stack_size() { + b = b.stack_size(stack_size); + } + let join_handle = b.spawn(|| options.run())?; + STYLE_THREAD_JOIN_HANDLES.lock().push(join_handle); + Ok(()) +} + +fn thread_startup(_index: usize) { + thread_state::initialize_layout_worker_thread(); + #[cfg(feature = "gecko")] + unsafe { + bindings::Gecko_SetJemallocThreadLocalArena(true); + let name = thread_name(_index); + gecko_profiler::register_thread(&name); + } +} + +fn thread_shutdown(_: usize) { + #[cfg(feature = "gecko")] + unsafe { + gecko_profiler::unregister_thread(); + bindings::Gecko_SetJemallocThreadLocalArena(false); + } +} + +impl StyleThreadPool { + /// Shuts down the thread pool, waiting for all work to complete. + pub fn shutdown() { + if STYLE_THREAD_JOIN_HANDLES.lock().is_empty() { + return; + } + { + // Drop the pool. + let _ = STYLE_THREAD_POOL.style_thread_pool.write().take(); + } + + // Join spawned threads until all of the threads have been joined. This + // will usually be pretty fast, as on shutdown there should be basically + // no threads left running. + while let Some(join_handle) = STYLE_THREAD_JOIN_HANDLES.lock().pop() { + let _ = join_handle.join(); + } + } + + /// Returns a reference to the thread pool. + /// + /// We only really want to give read-only access to the pool, except + /// for shutdown(). + pub fn pool(&self) -> RwLockReadGuard> { + self.style_thread_pool.read() + } + + /// Returns a list of the pool's platform-specific thread handles. + pub fn get_thread_handles(handles: &mut ThinVec) { + // Force the lazy initialization of STYLE_THREAD_POOL so that the threads get spawned and + // their join handles are added to STYLE_THREAD_JOIN_HANDLES. + lazy_static::initialize(&STYLE_THREAD_POOL); + + for join_handle in STYLE_THREAD_JOIN_HANDLES.lock().iter() { + #[cfg(unix)] + let handle = join_handle.as_pthread_t(); + #[cfg(windows)] + let handle = join_handle.as_raw_handle(); + + handles.push(handle); + } + } +} + +#[cfg(feature = "servo")] +fn stylo_threads_pref() -> i32 { + pref!(layout.threads) +} + +#[cfg(feature = "gecko")] +fn stylo_threads_pref() -> i32 { + static_prefs::pref!("layout.css.stylo-threads") +} + +/// The performance benefit of additional threads seems to level off at around six, so we cap it +/// there on many-core machines (see bug 1431285 comment 14). +pub(crate) const STYLO_MAX_THREADS: usize = 6; + +lazy_static! { + /// Global thread pool + pub static ref STYLE_THREAD_POOL: StyleThreadPool = { + use std::cmp; + // We always set this pref on startup, before layout or script have had a chance of + // accessing (and thus creating) the thread-pool. + let threads_pref: i32 = stylo_threads_pref(); + let num_threads = if threads_pref >= 0 { + threads_pref as usize + } else { + // Gecko may wish to override the default number of threads, for example on + // systems with heterogeneous CPUs. + #[cfg(feature = "gecko")] + let num_threads = unsafe { bindings::Gecko_GetNumStyleThreads() }; + #[cfg(not(feature = "gecko"))] + let num_threads = -1; + + if num_threads >= 0 { + num_threads as usize + } else { + use num_cpus; + // The default heuristic is num_virtual_cores * .75. This gives us three threads on a + // hyper-threaded dual core, and six threads on a hyper-threaded quad core. + cmp::max(num_cpus::get() * 3 / 4, 1) + } + }; + + let num_threads = cmp::min(num_threads, STYLO_MAX_THREADS); + // Since the main-thread is also part of the pool, having one thread or less doesn't make + // sense. + let (pool, num_threads) = if num_threads <= 1 { + (None, None) + } else { + let workers = rayon::ThreadPoolBuilder::new() + .spawn_handler(thread_spawn) + .use_current_thread() + .num_threads(num_threads) + .thread_name(thread_name) + .start_handler(thread_startup) + .exit_handler(thread_shutdown) + .stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024) + .build(); + (workers.ok(), Some(num_threads)) + }; + + StyleThreadPool { + num_threads, + style_thread_pool: RwLock::new(pool), + } + }; + + /// Global style data + pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData { + shared_lock: SharedRwLock::new_leaked(), + options: StyleSystemOptions::default(), + }; +} -- cgit v1.2.3