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.rs171
1 files changed, 171 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..2a00bccd77
--- /dev/null
+++ b/servo/components/style/global_style_data.rs
@@ -0,0 +1,171 @@
+/* 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;
+use std::{io, thread};
+
+/// 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<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)
+}
+
+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<Vec<thread::JoinHandle<()>>> =
+ 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<Option<rayon::ThreadPool>> {
+ self.style_thread_pool.read()
+ }
+}
+
+#[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")
+}
+
+lazy_static! {
+ /// Global thread pool
+ pub static ref STYLE_THREAD_POOL: StyleThreadPool = {
+ // 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 {
+ use num_cpus;
+ use std::cmp;
+ // 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).
+ let threads = cmp::min(cmp::max(num_cpus::get() * 3 / 4, 1), 6);
+ // There's no point in creating a thread pool if there's one thread.
+ if threads == 1 { 0 } else { threads }
+ };
+
+ let (pool, num_threads) = if num_threads < 1 {
+ (None, None)
+ } else {
+ let workers = rayon::ThreadPoolBuilder::new()
+ .spawn_handler(thread_spawn)
+ .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(),
+ };
+}