From 048d4a2a5a020f943221a83eeac4c90a7f6aa0c3 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Wed, 11 Jun 2025 11:50:20 +0200 Subject: [PATCH] libservo: Let libservo manage compositor message reception (#37372) Instead of receiving message in the compositor during a spin of the Servo event loop, receive them in libservo and then send them to the compositor. This is preparation for allowing libservo to wait for messages without spinning the main application event loop. This is useful for two situations: 1. Allowing a blocking shutdown mode, which can be used to ensure clean shutdown, regardless of how the API is used. 2. Allowing unit tests to wait until message are received instead of using a timer like they do now. Testing: This should not change behavior and is thus covered by existing tests. Fixes: This is part of #37371. Signed-off-by: Martin Robinson --- components/compositing/compositor.rs | 43 ++++++++++++++++------------ components/servo/lib.rs | 9 +++++- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 09e6ac0b317..b598ed939f9 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -2,7 +2,7 @@ * 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/. */ -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, Ref, RefCell}; use std::collections::HashMap; use std::env; use std::fs::create_dir_all; @@ -1651,40 +1651,45 @@ impl IOCompositor { ); } + /// Get the message receiver for this [`IOCompositor`]. + pub fn receiver(&self) -> Ref> { + Ref::map(self.global.borrow(), |global| &global.compositor_receiver) + } + #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - pub fn receive_messages(&mut self) { + pub fn handle_messages(&mut self, mut messages: Vec) { // Check for new messages coming from the other threads in the system. - let mut compositor_messages = vec![]; let mut found_recomposite_msg = false; - while let Ok(msg) = self.global.borrow_mut().compositor_receiver.try_recv() { - match msg { + messages.retain(|message| { + match message { CompositorMsg::NewWebRenderFrameReady(..) if found_recomposite_msg => { // Only take one of duplicate NewWebRendeFrameReady messages, but do subtract // one frame from the pending frames. self.pending_frames -= 1; + false }, CompositorMsg::NewWebRenderFrameReady(..) => { found_recomposite_msg = true; - compositor_messages.push(msg); + + // Process all pending events + // FIXME: Shouldn't `webview_frame_ready` be stored globally and why can't `pending_frames` + // be used here? + self.webview_renderers.iter().for_each(|webview| { + webview.dispatch_pending_point_input_events(); + webview.webrender_frame_ready.set(true); + }); + + true }, - _ => compositor_messages.push(msg), + _ => true, } - } - - if found_recomposite_msg { - // Process all pending events - self.webview_renderers.iter().for_each(|webview| { - webview.dispatch_pending_point_input_events(); - webview.webrender_frame_ready.set(true); - }); - } - - for msg in compositor_messages { - self.handle_browser_message(msg); + }); + for message in messages { + self.handle_browser_message(message); if self.global.borrow().shutdown_state() == ShutdownState::FinishedShuttingDown { return; } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index a188678c024..84967f22107 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -548,7 +548,14 @@ impl Servo { return false; } - self.compositor.borrow_mut().receive_messages(); + { + let mut compositor = self.compositor.borrow_mut(); + let mut messages = Vec::new(); + while let Ok(message) = compositor.receiver().try_recv() { + messages.push(message); + } + compositor.handle_messages(messages); + } // Only handle incoming embedder messages if the compositor hasn't already started shutting down. while let Ok(message) = self.embedder_receiver.try_recv() {