base: Allow sending GenericReceiver (#38636)

Implement Serialize and Deserialize for GenericReceiver to also allow
the Receiver to be sent across
ipc channels.
This is necessary to allow using the GenericChannel in more places.

Testing: Manually tested on follow-up feature branch. Does not require
new tests.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Jonathan Schwender 2025-08-14 20:28:58 +02:00 committed by GitHub
parent b5932e5abf
commit f492cbf8c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -9,6 +9,10 @@ use std::fmt;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
static GENERIC_CHANNEL_USAGE_ERROR_PANIC_MSG: &str = "May not send a crossbeam channel over an IPC channel. \
Please also convert the ipc-channel you want to send this GenericReceiver over \
into a GenericChannel.";
pub enum GenericSender<T: Serialize> { pub enum GenericSender<T: Serialize> {
Ipc(ipc_channel::ipc::IpcSender<T>), Ipc(ipc_channel::ipc::IpcSender<T>),
Crossbeam(crossbeam_channel::Sender<T>), Crossbeam(crossbeam_channel::Sender<T>),
@ -18,7 +22,7 @@ impl<T: Serialize> Serialize for GenericSender<T> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match self { match self {
GenericSender::Ipc(i) => i.serialize(s), GenericSender::Ipc(i) => i.serialize(s),
GenericSender::Crossbeam(_) => unreachable!(), GenericSender::Crossbeam(_) => panic!("{GENERIC_CHANNEL_USAGE_ERROR_PANIC_MSG}"),
} }
} }
} }
@ -28,8 +32,8 @@ impl<'a, T: Serialize> Deserialize<'a> for GenericSender<T> {
where where
D: Deserializer<'a>, D: Deserializer<'a>,
{ {
// Only ipc_channle will encounter deserialize scenario. // Only ipc_channel will encounter deserialize scenario.
ipc_channel::ipc::IpcSender::<T>::deserialize(d).map(|s| GenericSender::Ipc(s)) ipc_channel::ipc::IpcSender::<T>::deserialize(d).map(GenericSender::Ipc)
} }
} }
@ -81,6 +85,7 @@ impl<T> GenericReceiver<T>
where where
T: for<'de> Deserialize<'de> + Serialize, T: for<'de> Deserialize<'de> + Serialize,
{ {
#[inline]
pub fn recv(&self) -> ReceiveResult<T> { pub fn recv(&self) -> ReceiveResult<T> {
match *self { match *self {
GenericReceiver::Ipc(ref receiver) => receiver.recv().map_err(|_| ReceiveError), GenericReceiver::Ipc(ref receiver) => receiver.recv().map_err(|_| ReceiveError),
@ -88,6 +93,7 @@ where
} }
} }
#[inline]
pub fn try_recv(&self) -> ReceiveResult<T> { pub fn try_recv(&self) -> ReceiveResult<T> {
match *self { match *self {
GenericReceiver::Ipc(ref receiver) => receiver.try_recv().map_err(|_| ReceiveError), GenericReceiver::Ipc(ref receiver) => receiver.try_recv().map_err(|_| ReceiveError),
@ -97,6 +103,7 @@ where
} }
} }
#[inline]
pub fn into_inner(self) -> crossbeam_channel::Receiver<T> pub fn into_inner(self) -> crossbeam_channel::Receiver<T>
where where
T: Send + 'static, T: Send + 'static,
@ -110,6 +117,31 @@ where
} }
} }
impl<T> Serialize for GenericReceiver<T>
where
T: for<'de> Deserialize<'de> + Serialize,
{
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match self {
GenericReceiver::Ipc(receiver) => receiver.serialize(s),
GenericReceiver::Crossbeam(_) => panic!("{GENERIC_CHANNEL_USAGE_ERROR_PANIC_MSG}"),
}
}
}
impl<'a, T> Deserialize<'a> for GenericReceiver<T>
where
T: for<'de> Deserialize<'de> + Serialize,
{
fn deserialize<D>(d: D) -> Result<GenericReceiver<T>, D::Error>
where
D: Deserializer<'a>,
{
// Only ipc_channel will encounter deserialize scenario.
ipc_channel::ipc::IpcReceiver::<T>::deserialize(d).map(GenericReceiver::Ipc)
}
}
/// Creates a Servo channel that can select different channel implementations based on multiprocess /// Creates a Servo channel that can select different channel implementations based on multiprocess
/// mode or not. If the scenario doesn't require message to pass process boundary, a simple /// mode or not. If the scenario doesn't require message to pass process boundary, a simple
/// crossbeam channel is preferred. /// crossbeam channel is preferred.