script: Move TaskManager to GlobalScope (#34827)

This is a simplification of the internal `TaskQueue` API that moves the
`TaskManager` to the `GlobalScope` itself. In addition, the handling of
cancellers is moved to the `TaskManager` as well. This means that no
arguments other than the `task` are necessary for queueing tasks, which
makes the API a lot easier to use and cleaner.

`TaskSource` now also keeps a copy of the canceller with it, so that
they always know the proper way to cancel any tasks queued on them.

There is one complication here. The event loop `sender` for dedicated
workers is constantly changing as it is set to `None` when not handling
messages. This is because this sender keeps a handle to the main
thread's `Worker` object, preventing garbage collection while any
messages are still in flight or being handled. This change allows
setting the `sender` on the `TaskManager` to `None` to allow proper
garbabge collection.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-01-04 09:41:50 +01:00 committed by GitHub
parent 75a22cfe2e
commit b2eda71952
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 1060 additions and 1516 deletions

View file

@ -11,7 +11,6 @@ use std::default::Default;
use std::io::{stderr, stdout, Write};
use std::ptr::NonNull;
use std::rc::Rc;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
@ -153,12 +152,9 @@ use crate::messaging::{
};
use crate::microtask::MicrotaskQueue;
use crate::realms::{enter_realm, InRealm};
use crate::script_runtime::{
CanGc, CommonScriptMsg, JSContext, Runtime, ScriptChan, ScriptPort, ScriptThreadEventCategory,
};
use crate::script_runtime::{CanGc, JSContext, Runtime, ScriptChan, ScriptPort};
use crate::script_thread::ScriptThread;
use crate::task_manager::TaskManager;
use crate::task_source::TaskSourceName;
use crate::timers::{IsInterval, TimerCallback};
use crate::unminify::unminified_path;
use crate::webdriver_handlers::jsval_to_webdriver;
@ -208,7 +204,6 @@ pub struct Window {
globalscope: GlobalScope,
#[ignore_malloc_size_of = "trait objects are hard"]
script_chan: MainThreadScriptChan,
task_manager: TaskManager,
#[no_trace]
#[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
layout: RefCell<Box<dyn Layout>>,
@ -383,10 +378,6 @@ pub struct Window {
}
impl Window {
pub fn task_manager(&self) -> &TaskManager {
&self.task_manager
}
pub fn layout(&self) -> Ref<Box<dyn Layout>> {
self.layout.borrow()
}
@ -411,7 +402,8 @@ impl Window {
*self.js_runtime.borrow_for_script_deallocation() = None;
self.window_proxy.set(None);
self.current_state.set(WindowState::Zombie);
self.ignore_all_tasks();
self.task_manager()
.cancel_all_tasks_and_ignore_future_tasks();
}
}
@ -426,16 +418,8 @@ impl Window {
// Step 4 of https://html.spec.whatwg.org/multipage/#discard-a-document
// Other steps performed when the `PipelineExit` message
// is handled by the ScriptThread.
self.ignore_all_tasks();
}
/// Cancel all current, and ignore all subsequently queued, tasks.
pub fn ignore_all_tasks(&self) {
let mut ignore_flags = self.task_manager.task_cancellers.borrow_mut();
for task_source_name in TaskSourceName::all() {
let flag = ignore_flags.entry(*task_source_name).or_default();
flag.store(true, Ordering::SeqCst);
}
self.task_manager()
.cancel_all_tasks_and_ignore_future_tasks();
}
/// Get a sender to the time profiler thread.
@ -572,6 +556,10 @@ impl Window {
pub fn dispatch_event_with_target_override(&self, event: &Event, can_gc: CanGc) -> EventStatus {
event.dispatch(self.upcast(), true, can_gc)
}
pub(crate) fn task_manager(&self) -> &TaskManager {
self.upcast::<GlobalScope>().task_manager()
}
}
// https://html.spec.whatwg.org/multipage/#atob
@ -825,9 +813,10 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
window.send_to_constellation(ScriptMsg::DiscardTopLevelBrowsingContext);
}
});
self.task_manager()
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task, self.upcast::<GlobalScope>())
.queue(task)
.expect("Queuing window_close_browsing_context task to work");
}
}
@ -1657,29 +1646,6 @@ impl Window {
self.document.get().is_some()
}
/// Cancels all the tasks associated with that window.
///
/// This sets the current `task_manager.task_cancellers` sentinel value to
/// `true` and replaces it with a brand new one for future tasks.
pub fn cancel_all_tasks(&self) {
let mut ignore_flags = self.task_manager.task_cancellers.borrow_mut();
for task_source_name in TaskSourceName::all() {
let flag = ignore_flags.entry(*task_source_name).or_default();
let cancelled = std::mem::take(&mut *flag);
cancelled.store(true, Ordering::SeqCst);
}
}
/// Cancels all the tasks from a given task source.
/// This sets the current sentinel value to
/// `true` and replaces it with a brand new one for future tasks.
pub fn cancel_all_tasks_from_source(&self, task_source_name: TaskSourceName) {
let mut ignore_flags = self.task_manager.task_cancellers.borrow_mut();
let flag = ignore_flags.entry(task_source_name).or_default();
let cancelled = std::mem::take(&mut *flag);
cancelled.store(true, Ordering::SeqCst);
}
pub fn clear_js_runtime(&self) {
self.upcast::<GlobalScope>()
.remove_web_messaging_and_dedicated_workers_infra();
@ -1721,7 +1687,8 @@ impl Window {
if let Some(performance) = self.performance.get() {
performance.clear_and_disable_performance_entry_buffer();
}
self.ignore_all_tasks();
self.task_manager()
.cancel_all_tasks_and_ignore_future_tasks();
}
/// <https://drafts.csswg.org/cssom-view/#dom-window-scroll>
@ -2368,17 +2335,10 @@ impl Window {
CanGc::note());
event.upcast::<Event>().fire(this.upcast::<EventTarget>(), CanGc::note());
});
// FIXME(nox): Why are errors silenced here?
let _ = self.script_chan.send(CommonScriptMsg::Task(
ScriptThreadEventCategory::DomEvent,
Box::new(
self.task_manager
.task_canceller(TaskSourceName::DOMManipulation)
.wrap_task(task),
),
Some(self.pipeline_id()),
TaskSourceName::DOMManipulation,
));
let _ = self
.task_manager()
.dom_manipulation_task_source()
.queue(task);
doc.set_url(load_data.url.clone());
return;
}
@ -2715,7 +2675,6 @@ impl Window {
pub(crate) fn new(
runtime: Rc<Runtime>,
script_chan: MainThreadScriptChan,
task_manager: TaskManager,
layout: Box<dyn Layout>,
image_cache_chan: Sender<ImageCacheMsg>,
image_cache: Arc<dyn ImageCache>,
@ -2726,7 +2685,7 @@ impl Window {
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
constellation_chan: ScriptToConstellationChan,
control_chan: IpcSender<ConstellationControlMsg>,
pipelineid: PipelineId,
pipeline_id: PipelineId,
parent_info: Option<PipelineId>,
window_size: WindowSizeData,
origin: MutableOrigin,
@ -2751,7 +2710,7 @@ impl Window {
inherited_secure_context: Option<bool>,
) -> DomRoot<Self> {
let error_reporter = CSSErrorReporter {
pipelineid,
pipelineid: pipeline_id,
script_chan: Arc::new(Mutex::new(control_chan)),
};
@ -2762,7 +2721,7 @@ impl Window {
let win = Box::new(Self {
globalscope: GlobalScope::new_inherited(
pipelineid,
pipeline_id,
devtools_chan,
mem_profiler_chan,
time_profiler_chan,
@ -2779,7 +2738,6 @@ impl Window {
unminify_js,
),
script_chan,
task_manager,
layout: RefCell::new(layout),
image_cache_chan,
image_cache,
@ -2840,6 +2798,10 @@ impl Window {
unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) }
}
pub(crate) fn event_loop_sender(&self) -> Box<dyn ScriptChan + Send> {
Box::new(self.script_chan.clone())
}
pub fn pipeline_id(&self) -> PipelineId {
self.upcast::<GlobalScope>().pipeline_id()
}
@ -2983,19 +2945,11 @@ impl Window {
);
}
});
// FIXME(nox): Why are errors silenced here?
// TODO(#12718): Use the "posted message task source".
// TODO: When switching to the right task source, update the task_canceller call too.
let _ = self.script_chan.send(CommonScriptMsg::Task(
ScriptThreadEventCategory::DomEvent,
Box::new(
self.task_manager
.task_canceller(TaskSourceName::DOMManipulation)
.wrap_task(task),
),
Some(self.pipeline_id()),
TaskSourceName::DOMManipulation,
));
let _ = self
.task_manager()
.dom_manipulation_task_source()
.queue(task);
}
}