script: Reduce ScriptThread TLS usage (#38875)

We store a pointer to the ScriptThread singleton for a thread in
thread-local storage. While we don't have yet have profiles pointing to
this TLS access as a hot spot, we can remove a potential performance
footgun without a lot of effort by passing around small pieces of data
that we otherwise need to fetch from the ScriptThread.

Testing: Existing WPT is sufficient
Fixes: part of #37969

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2025-08-30 12:51:40 -04:00 committed by GitHub
parent d1da1a995c
commit c97ec1b2fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 129 additions and 68 deletions

View file

@ -279,7 +279,7 @@ pub struct ScriptThread {
docs_with_no_blocking_loads: DomRefCell<HashSet<Dom<Document>>>,
/// <https://html.spec.whatwg.org/multipage/#custom-element-reactions-stack>
custom_element_reaction_stack: CustomElementReactionStack,
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
/// Cross-process access to the compositor's API.
#[no_trace]
@ -723,24 +723,21 @@ impl ScriptThread {
with_script_thread(|script_thread| script_thread.is_user_interacting.get())
}
pub(crate) fn get_fully_active_document_ids() -> HashSet<PipelineId> {
with_script_thread(|script_thread| {
script_thread
.documents
.borrow()
.iter()
.filter_map(|(id, document)| {
if document.is_fully_active() {
Some(id)
} else {
None
}
})
.fold(HashSet::new(), |mut set, id| {
let _ = set.insert(id);
set
})
})
pub(crate) fn get_fully_active_document_ids(&self) -> HashSet<PipelineId> {
self.documents
.borrow()
.iter()
.filter_map(|(id, document)| {
if document.is_fully_active() {
Some(id)
} else {
None
}
})
.fold(HashSet::new(), |mut set, id| {
let _ = set.insert(id);
set
})
}
pub(crate) fn find_window_proxy(id: BrowsingContextId) -> Option<DomRoot<WindowProxy>> {
@ -810,19 +807,13 @@ impl ScriptThread {
.register_paint_worklet_modules(name, properties, painter);
}
pub(crate) fn push_new_element_queue() {
with_script_thread(|script_thread| {
pub(crate) fn custom_element_reaction_stack() -> Rc<CustomElementReactionStack> {
with_optional_script_thread(|script_thread| {
script_thread
.as_ref()
.unwrap()
.custom_element_reaction_stack
.push_new_element_queue();
})
}
pub(crate) fn pop_current_element_queue(can_gc: CanGc) {
with_script_thread(|script_thread| {
script_thread
.custom_element_reaction_stack
.pop_current_element_queue(can_gc);
.clone()
})
}
@ -1006,7 +997,7 @@ impl ScriptThread {
webxr_registry: state.webxr_registry,
worklet_thread_pool: Default::default(),
docs_with_no_blocking_loads: Default::default(),
custom_element_reaction_stack: CustomElementReactionStack::new(),
custom_element_reaction_stack: Rc::new(CustomElementReactionStack::new()),
compositor_api: state.compositor_api,
profile_script_events: opts.debug.profile_script_events,
print_pwm: opts.print_pwm,
@ -1352,9 +1343,12 @@ impl ScriptThread {
// Receive at least one message so we don't spinloop.
debug!("Waiting for event.");
let mut event = self
.receivers
.recv(&self.task_queue, &self.timer_scheduler.borrow());
let fully_active = self.get_fully_active_document_ids();
let mut event = self.receivers.recv(
&self.task_queue,
&self.timer_scheduler.borrow(),
&fully_active,
);
loop {
debug!("Handling event: {event:?}");
@ -1464,7 +1458,7 @@ impl ScriptThread {
// If any of our input sources has an event pending, we'll perform another iteration
// and check for more resize events. If there are no events pending, we'll move
// on and execute the sequential non-resize events we've seen.
match self.receivers.try_recv(&self.task_queue) {
match self.receivers.try_recv(&self.task_queue, &fully_active) {
Some(new_event) => event = new_event,
None => break,
}
@ -3422,6 +3416,7 @@ impl ScriptThread {
true,
incomplete.load_data.inherited_insecure_requests_policy,
incomplete.load_data.has_trustworthy_ancestor_origin,
self.custom_element_reaction_stack.clone(),
can_gc,
);