Add generic cross process callback mechanism (#38973)

One commonly encountered mechanism in servo is using ipc channels
together with the router, to register a custom callback to run in the
current process, when receiving a reply.
The new `GenericCallback` abstracts over this, and allows executing an
arbitrary callback in the process of the `GenericCallback` creator. In
multiprocess mode, this internally uses ipc channels and follows the
existing pattern.
In single process mode, we execute the callback directly, which avoids
one call to the router.
Executing the callback still incurs synchronization, since we need to
support cloning the abstraction, and the callback closure may be
`FnMut`. Future work could provide more optimized abstractions for
callbacks that don't have these requirements.

This PR allows applying #38782 again, which was previously reverted in
#38940 due to the lack of custom callback support.

See also the module documentation in `generic_channel/callback.rs`.

Testing: This PR adds unit tests. Also passes the manual testcase from
#38939

Part of #38912

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Jonathan Schwender 2025-08-30 18:58:49 +02:00 committed by GitHub
parent 6565d982bd
commit e5f9d81058
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 366 additions and 20 deletions

View file

@ -24,7 +24,7 @@ pub mod viewport_description;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use base::generic_channel::{self, GenericSender};
use base::generic_channel::{self, GenericCallback, GenericSender};
use bitflags::bitflags;
use display_list::CompositorDisplayListInfo;
use embedder_traits::ScreenGeometry;
@ -188,19 +188,19 @@ pub struct CompositionPipeline {
/// A mechanism to send messages from ScriptThread to the parent process' WebRender instance.
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct CrossProcessCompositorApi(IpcSender<CompositorMsg>);
pub struct CrossProcessCompositorApi(GenericCallback<CompositorMsg>);
impl CrossProcessCompositorApi {
/// Create a new [`CrossProcessCompositorApi`] struct.
pub fn new(sender: IpcSender<CompositorMsg>) -> Self {
CrossProcessCompositorApi(sender)
pub fn new(callback: GenericCallback<CompositorMsg>) -> Self {
CrossProcessCompositorApi(callback)
}
/// Create a new [`CrossProcessCompositorApi`] struct that does not have a listener on the other
/// end to use for unit testing.
pub fn dummy() -> Self {
let (sender, _) = ipc::channel().unwrap();
Self(sender)
let callback = GenericCallback::new(|_msg| ()).unwrap();
Self(callback)
}
/// Inform WebRender of the existence of this pipeline.