use std::fmt;
use std::ops::Deref;
use std::sync::Arc;
use ipc_channel::ipc::IpcSharedMemory;
use malloc_size_of::MallocSizeOf;
use serde::de::VariantAccess;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use servo_config::opts;
#[derive(Clone)]
pub struct GenericSharedMemory(GenericSharedMemoryVariant);
#[derive(Clone)]
enum GenericSharedMemoryVariant {
Ipc(IpcSharedMemory),
InProcess(Arc<Vec<u8>>),
}
impl Deref for GenericSharedMemory {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
match &self.0 {
GenericSharedMemoryVariant::Ipc(ipc_shared_memory) => ipc_shared_memory,
GenericSharedMemoryVariant::InProcess(items) => items.as_slice(),
}
}
}
impl MallocSizeOf for GenericSharedMemory {
fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
match &self.0 {
GenericSharedMemoryVariant::Ipc(_) => 0,
GenericSharedMemoryVariant::InProcess(items) => items.size_of(ops),
}
}
}
impl GenericSharedMemory {
pub fn from_bytes(bytes: &[u8]) -> Self {
if servo_config::opts::get().multiprocess || servo_config::opts::get().force_ipc {
GenericSharedMemory(GenericSharedMemoryVariant::Ipc(
IpcSharedMemory::from_bytes(bytes),
))
} else {
GenericSharedMemory(GenericSharedMemoryVariant::InProcess(Arc::new(
bytes.to_owned(),
)))
}
}
pub fn from_byte(data: u8, length: usize) -> Self {
if servo_config::opts::get().multiprocess || servo_config::opts::get().force_ipc {
GenericSharedMemory(GenericSharedMemoryVariant::Ipc(IpcSharedMemory::from_byte(
data, length,
)))
} else {
GenericSharedMemory(GenericSharedMemoryVariant::InProcess(Arc::new(vec![
data;
length
])))
}
}
}
impl fmt::Debug for GenericSharedMemory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("GenericSharedMemory").finish()
}
}
impl Serialize for GenericSharedMemory {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match &self.0 {
GenericSharedMemoryVariant::Ipc(memory) => {
s.serialize_newtype_variant("GenericSharedMemory", 0, "Ipc", memory)
},
GenericSharedMemoryVariant::InProcess(arc) => {
if opts::get().multiprocess || opts::get().force_ipc {
return Err(serde::ser::Error::custom(
"Arc<Vec<u8>> found in multiprocess mode!",
));
} let address = Arc::into_raw(arc.clone()) as *mut Vec<u8> as usize;
s.serialize_newtype_variant("GenericSharedMemory", 1, "InProcess", &address)
},
}
}
}
struct GenericSharedMemoryVisitor {}
impl<'de> serde::de::Visitor<'de> for GenericSharedMemoryVisitor {
type Value = GenericSharedMemory;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a GenericReceiver variant")
}
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
where
A: serde::de::EnumAccess<'de>,
{
#[derive(Deserialize)]
enum GenericSharedMemoryVariantNames {
Ipc,
InProcess,
}
let (variant_name, variant_data): (GenericSharedMemoryVariantNames, _) = data.variant()?;
match variant_name {
GenericSharedMemoryVariantNames::Ipc => variant_data
.newtype_variant::<IpcSharedMemory>()
.map(|receiver| GenericSharedMemory(GenericSharedMemoryVariant::Ipc(receiver))),
GenericSharedMemoryVariantNames::InProcess => {
if opts::get().multiprocess || servo_config::opts::get().force_ipc {
return Err(serde::de::Error::custom(
"Arc data found in multiprocess mode!",
));
}
let addr = variant_data.newtype_variant::<usize>()?;
let ptr = addr as *mut Vec<u8>;
#[expect(unsafe_code)]
let arc = unsafe { Arc::from_raw(ptr) };
Ok(GenericSharedMemory(GenericSharedMemoryVariant::InProcess(
arc,
)))
},
}
}
}
impl<'a> Deserialize<'a> for GenericSharedMemory {
fn deserialize<D>(d: D) -> Result<GenericSharedMemory, D::Error>
where
D: Deserializer<'a>,
{
d.deserialize_enum(
"GenericSharedMemory",
&["Ipc", "InProcess"],
GenericSharedMemoryVisitor {},
)
}
}
#[cfg(test)]
mod single_process_shared_memory_test {
use std::sync::Arc;
use ipc_channel::ipc::IpcSharedMemory;
use super::GenericSharedMemory;
use crate::generic_channel::{self};
#[test]
fn test_ipc() {
let bytes = vec![0xba; 10];
let bytes_copy = bytes.clone();
let shared_memory = GenericSharedMemory(super::GenericSharedMemoryVariant::Ipc(
IpcSharedMemory::from_bytes(&bytes),
));
let (send, recv) = generic_channel::channel().unwrap();
send.send(shared_memory).expect("Could not send");
assert_eq!(recv.recv().unwrap().to_vec(), bytes_copy);
}
#[test]
fn test_inprocess() {
let bytes = vec![0xba; 10];
let bytes_copy = bytes.clone();
let shared_memory = GenericSharedMemory(super::GenericSharedMemoryVariant::InProcess(
Arc::new(bytes.clone()),
));
let (send, recv) = generic_channel::channel().unwrap();
send.send(shared_memory).expect("Could not send");
assert_eq!(recv.recv().unwrap().to_vec(), bytes_copy);
}
}