script: Move TimerListener creation to OneShotTimers (#34825)

Before each `OneShotTimers` would have a `TimerListener` for its
lifetime, holding the timer `TaskSource`. The issue with this is that
the `TaskSource` for dedicated workers keeps the main thread object
alive, so as long as the `OneShotTimers` alive (until the worker thread
exists), the main thread object would never be garbage collected.

This change makes the creation of the listener on-demand, avoiding the
long-lived handle to the main thread object and slightly simplifying
`OneShotTimers` at the expense of some more operations when scheduling a
timer.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-01-04 12:23:43 +01:00 committed by GitHub
parent b2eda71952
commit 5b6c75e358
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 56 deletions

View file

@ -60,7 +60,7 @@ use script_traits::{
PortMessageTask, ScriptMsg, ScriptToConstellationChan,
};
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
use timers::{BoxedTimerCallback, TimerEvent, TimerEventId, TimerEventRequest, TimerSource};
use timers::{TimerEventId, TimerEventRequest, TimerSource};
use uuid::Uuid;
#[cfg(feature = "webgpu")]
use webgpu::{DeviceLostReason, WebGPUDevice};
@ -386,13 +386,6 @@ struct BroadcastListener {
context: Trusted<GlobalScope>,
}
/// A wrapper between timer events coming in over IPC, and the event-loop.
#[derive(Clone)]
pub(crate) struct TimerListener {
task_source: TaskSource,
context: Trusted<GlobalScope>,
}
type FileListenerCallback = Box<dyn Fn(Rc<Promise>, Fallible<Vec<u8>>) + Send>;
/// A wrapper for the handling of file data received by the ipc router
@ -525,37 +518,6 @@ impl BroadcastListener {
}
}
impl TimerListener {
/// Handle a timer-event coming from the [`timers::TimerScheduler`]
/// by queuing the appropriate task on the relevant event-loop.
fn handle(&self, event: TimerEvent) {
let context = self.context.clone();
// Step 18, queue a task,
// https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
let _ = self.task_source.queue(
task!(timer_event: move || {
let global = context.root();
let TimerEvent(source, id) = event;
match source {
TimerSource::FromWorker => {
global.downcast::<WorkerGlobalScope>().expect("Window timer delivered to worker");
},
TimerSource::FromWindow(pipeline) => {
assert_eq!(pipeline, global.pipeline_id());
global.downcast::<Window>().expect("Worker timer delivered to window");
},
};
// Step 7, substeps run in a task.
global.fire_timer(id, CanGc::note());
})
);
}
pub fn into_callback(self) -> BoxedTimerCallback {
Box::new(move |timer_event| self.handle(timer_event))
}
}
impl MessageListener {
/// A new message came in, handle it via a task enqueued on the event-loop.
/// A task is required, since we are using a trusted globalscope,
@ -825,15 +787,7 @@ impl GlobalScope {
}
fn timers(&self) -> &OneshotTimers {
self.timers.get_or_init(|| {
OneshotTimers::new(
self,
TimerListener {
context: Trusted::new(self),
task_source: self.task_manager().timer_task_source(),
},
)
})
self.timers.get_or_init(|| OneshotTimers::new(self))
}
/// <https://w3c.github.io/ServiceWorker/#get-the-service-worker-registration-object>