summaryrefslogtreecommitdiffstats
path: root/servo/components/style/global_style_data.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/global_style_data.rs')
-rw-r--r--servo/components/style/global_style_data.rs173
1 files changed, 173 insertions, 0 deletions
diff --git a/servo/components/style/global_style_data.rs b/servo/components/style/global_style_data.rs
new file mode 100644
index 0000000000..a9e79bf682
--- /dev/null
+++ b/servo/components/style/global_style_data.rs
@@ -0,0 +1,173 @@
+/* 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 parking_lot::{RwLock, RwLockReadGuard};
+use rayon;
+use std::env;
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+/// 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.
+ pub num_threads: usize,
+
+ /// 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<Option<rayon::ThreadPool>>,
+}
+
+fn thread_name(index: usize) -> String {
+ format!("StyleThread#{}", index)
+}
+
+// A counter so that we can wait for shutdown of all threads. See
+// StyleThreadPool::shutdown.
+static ALIVE_WORKER_THREADS: AtomicUsize = AtomicUsize::new(0);
+
+fn thread_startup(_index: usize) {
+ ALIVE_WORKER_THREADS.fetch_add(1, Ordering::Relaxed);
+ thread_state::initialize_layout_worker_thread();
+ #[cfg(feature = "gecko")]
+ unsafe {
+ use std::ffi::CString;
+
+ bindings::Gecko_SetJemallocThreadLocalArena(true);
+ let name = thread_name(_index);
+ let name = CString::new(name).unwrap();
+ // Gecko_RegisterProfilerThread copies the passed name here.
+ bindings::Gecko_RegisterProfilerThread(name.as_ptr());
+ }
+}
+
+fn thread_shutdown(_: usize) {
+ #[cfg(feature = "gecko")]
+ unsafe {
+ bindings::Gecko_UnregisterProfilerThread();
+ bindings::Gecko_SetJemallocThreadLocalArena(false);
+ }
+ ALIVE_WORKER_THREADS.fetch_sub(1, Ordering::Relaxed);
+}
+
+impl StyleThreadPool {
+ /// Shuts down the thread pool, waiting for all work to complete.
+ pub fn shutdown() {
+ if ALIVE_WORKER_THREADS.load(Ordering::Relaxed) == 0 {
+ return;
+ }
+ {
+ // Drop the pool.
+ let _ = STYLE_THREAD_POOL.style_thread_pool.write().take();
+ }
+ // Spin until all our threads are done. This will usually be pretty
+ // fast, as on shutdown there should be basically no threads left
+ // running.
+ //
+ // This still _technically_ doesn't give us the guarantee of TLS
+ // destructors running on the worker threads. For that we'd need help
+ // from rayon to properly join the threads.
+ //
+ // See https://github.com/rayon-rs/rayon/issues/688
+ //
+ // So we instead intentionally leak TLS stuff (see BLOOM_KEY and co) for
+ // now until that's fixed.
+ while ALIVE_WORKER_THREADS.load(Ordering::Relaxed) != 0 {
+ std::thread::yield_now();
+ }
+ }
+
+ /// 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<Option<rayon::ThreadPool>> {
+ self.style_thread_pool.read()
+ }
+}
+
+lazy_static! {
+ /// Global thread pool
+ pub static ref STYLE_THREAD_POOL: StyleThreadPool = {
+ let stylo_threads = env::var("STYLO_THREADS")
+ .map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS value"));
+ let mut num_threads = match stylo_threads {
+ Ok(num) => num,
+ #[cfg(feature = "servo")]
+ _ => {
+ use servo_config::pref;
+ // We always set this pref on startup, before layout or script
+ // have had a chance of accessing (and thus creating) the
+ // thread-pool.
+ pref!(layout.threads) as usize
+ }
+ #[cfg(feature = "gecko")]
+ _ => {
+ // 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. 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).
+ use num_cpus;
+ use std::cmp;
+ cmp::min(cmp::max(num_cpus::get() * 3 / 4, 1), 6)
+ }
+ };
+
+ // If num_threads is one, there's no point in creating a thread pool, so
+ // force it to zero.
+ //
+ // We allow developers to force a one-thread pool for testing via a
+ // special environmental variable.
+ if num_threads == 1 {
+ let force_pool = env::var("FORCE_STYLO_THREAD_POOL")
+ .ok().map_or(false, |s| s.parse::<usize>().expect("invalid FORCE_STYLO_THREAD_POOL value") == 1);
+ if !force_pool {
+ num_threads = 0;
+ }
+ }
+
+ let pool = if num_threads < 1 {
+ None
+ } else {
+ let workers = rayon::ThreadPoolBuilder::new()
+ .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()
+ };
+
+ 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(),
+ };
+}