Source code

Revision control

Other Tools

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5
//! Global style data
6
7
use crate::context::StyleSystemOptions;
8
#[cfg(feature = "gecko")]
9
use crate::gecko_bindings::bindings;
10
use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
11
use crate::shared_lock::SharedRwLock;
12
use crate::thread_state;
13
use parking_lot::{RwLock, RwLockReadGuard};
14
use rayon;
15
use std::env;
16
use std::sync::atomic::{AtomicUsize, Ordering};
17
18
/// Global style data
19
pub struct GlobalStyleData {
20
/// Shared RWLock for CSSOM objects
21
pub shared_lock: SharedRwLock,
22
23
/// Global style system options determined by env vars.
24
pub options: StyleSystemOptions,
25
}
26
27
/// Global thread pool.
28
pub struct StyleThreadPool {
29
/// How many threads parallel styling can use.
30
pub num_threads: usize,
31
32
/// The parallel styling thread pool.
33
///
34
/// For leak-checking purposes, we want to terminate the thread-pool, which
35
/// waits for all the async jobs to complete. Thus the RwLock.
36
style_thread_pool: RwLock<Option<rayon::ThreadPool>>,
37
}
38
39
fn thread_name(index: usize) -> String {
40
format!("StyleThread#{}", index)
41
}
42
43
// A counter so that we can wait for shutdown of all threads. See
44
// StyleThreadPool::shutdown.
45
static ALIVE_WORKER_THREADS: AtomicUsize = AtomicUsize::new(0);
46
47
fn thread_startup(_index: usize) {
48
ALIVE_WORKER_THREADS.fetch_add(1, Ordering::Relaxed);
49
thread_state::initialize_layout_worker_thread();
50
#[cfg(feature = "gecko")]
51
unsafe {
52
use std::ffi::CString;
53
54
bindings::Gecko_SetJemallocThreadLocalArena(true);
55
let name = thread_name(_index);
56
let name = CString::new(name).unwrap();
57
// Gecko_RegisterProfilerThread copies the passed name here.
58
bindings::Gecko_RegisterProfilerThread(name.as_ptr());
59
}
60
}
61
62
fn thread_shutdown(_: usize) {
63
#[cfg(feature = "gecko")]
64
unsafe {
65
bindings::Gecko_UnregisterProfilerThread();
66
bindings::Gecko_SetJemallocThreadLocalArena(false);
67
}
68
ALIVE_WORKER_THREADS.fetch_sub(1, Ordering::Relaxed);
69
}
70
71
impl StyleThreadPool {
72
/// Shuts down the thread pool, waiting for all work to complete.
73
pub fn shutdown() {
74
if ALIVE_WORKER_THREADS.load(Ordering::Relaxed) == 0 {
75
return;
76
}
77
{
78
// Drop the pool.
79
let _ = STYLE_THREAD_POOL.style_thread_pool.write().take();
80
}
81
// Spin until all our threads are done. This will usually be pretty
82
// fast, as on shutdown there should be basically no threads left
83
// running.
84
//
85
// This still _technically_ doesn't give us the guarantee of TLS
86
// destructors running on the worker threads. For that we'd need help
87
// from rayon to properly join the threads.
88
//
90
//
91
// So we instead intentionally leak TLS stuff (see BLOOM_KEY and co) for
92
// now until that's fixed.
93
while ALIVE_WORKER_THREADS.load(Ordering::Relaxed) != 0 {
94
std::thread::yield_now();
95
}
96
}
97
98
/// Returns a reference to the thread pool.
99
///
100
/// We only really want to give read-only access to the pool, except
101
/// for shutdown().
102
pub fn pool(&self) -> RwLockReadGuard<Option<rayon::ThreadPool>> {
103
self.style_thread_pool.read()
104
}
105
}
106
107
lazy_static! {
108
/// Global thread pool
109
pub static ref STYLE_THREAD_POOL: StyleThreadPool = {
110
let stylo_threads = env::var("STYLO_THREADS")
111
.map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS value"));
112
let mut num_threads = match stylo_threads {
113
Ok(num) => num,
114
#[cfg(feature = "servo")]
115
_ => {
116
use servo_config::pref;
117
// We always set this pref on startup, before layout or script
118
// have had a chance of accessing (and thus creating) the
119
// thread-pool.
120
pref!(layout.threads) as usize
121
}
122
#[cfg(feature = "gecko")]
123
_ => {
124
// The default heuristic is num_virtual_cores * .75. This gives
125
// us three threads on a hyper-threaded dual core, and six
126
// threads on a hyper-threaded quad core. The performance
127
// benefit of additional threads seems to level off at around
128
// six, so we cap it there on many-core machines
129
// (see bug 1431285 comment 14).
130
use num_cpus;
131
use std::cmp;
132
cmp::min(cmp::max(num_cpus::get() * 3 / 4, 1), 6)
133
}
134
};
135
136
// If num_threads is one, there's no point in creating a thread pool, so
137
// force it to zero.
138
//
139
// We allow developers to force a one-thread pool for testing via a
140
// special environmental variable.
141
if num_threads == 1 {
142
let force_pool = env::var("FORCE_STYLO_THREAD_POOL")
143
.ok().map_or(false, |s| s.parse::<usize>().expect("invalid FORCE_STYLO_THREAD_POOL value") == 1);
144
if !force_pool {
145
num_threads = 0;
146
}
147
}
148
149
let pool = if num_threads < 1 {
150
None
151
} else {
152
let workers = rayon::ThreadPoolBuilder::new()
153
.num_threads(num_threads)
154
.thread_name(thread_name)
155
.start_handler(thread_startup)
156
.exit_handler(thread_shutdown)
157
.stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024)
158
.build();
159
workers.ok()
160
};
161
162
StyleThreadPool {
163
num_threads,
164
style_thread_pool: RwLock::new(pool),
165
}
166
};
167
168
/// Global style data
169
pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData {
170
shared_lock: SharedRwLock::new_leaked(),
171
options: StyleSystemOptions::default(),
172
};
173
}