Skip to main content

servo_base/generic_channel/
shared_memory.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::fmt;
6use std::ops::Deref;
7use std::sync::Arc;
8
9use ipc_channel::ipc::IpcSharedMemory;
10use malloc_size_of::MallocSizeOf;
11use serde::de::VariantAccess;
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13use servo_config::opts;
14
15#[derive(Clone)]
16pub struct GenericSharedMemory(GenericSharedMemoryVariant);
17
18#[derive(Clone)]
19enum GenericSharedMemoryVariant {
20    Ipc(IpcSharedMemory),
21    InProcess(Arc<Vec<u8>>),
22}
23
24impl AsRef<[u8]> for GenericSharedMemory {
25    fn as_ref(&self) -> &[u8] {
26        self
27    }
28}
29
30impl Deref for GenericSharedMemory {
31    type Target = [u8];
32
33    #[inline]
34    fn deref(&self) -> &[u8] {
35        match &self.0 {
36            GenericSharedMemoryVariant::Ipc(ipc_shared_memory) => ipc_shared_memory,
37            GenericSharedMemoryVariant::InProcess(items) => items.as_slice(),
38        }
39    }
40}
41
42impl MallocSizeOf for GenericSharedMemory {
43    fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
44        match &self.0 {
45            GenericSharedMemoryVariant::Ipc(_) => 0,
46            GenericSharedMemoryVariant::InProcess(items) => items.size_of(ops),
47        }
48    }
49}
50
51impl GenericSharedMemory {
52    pub fn from_bytes(bytes: &[u8]) -> Self {
53        if servo_config::opts::get().multiprocess || servo_config::opts::get().force_ipc {
54            GenericSharedMemory(GenericSharedMemoryVariant::Ipc(
55                IpcSharedMemory::from_bytes(bytes),
56            ))
57        } else {
58            GenericSharedMemory(GenericSharedMemoryVariant::InProcess(Arc::new(
59                bytes.to_owned(),
60            )))
61        }
62    }
63
64    pub fn from_byte(data: u8, length: usize) -> Self {
65        if servo_config::opts::get().multiprocess || servo_config::opts::get().force_ipc {
66            GenericSharedMemory(GenericSharedMemoryVariant::Ipc(IpcSharedMemory::from_byte(
67                data, length,
68            )))
69        } else {
70            GenericSharedMemory(GenericSharedMemoryVariant::InProcess(Arc::new(vec![
71                data;
72                length
73            ])))
74        }
75    }
76
77    /// Build a `GenericSharedMemory` from a `Vec<u8>`.
78    ///
79    /// In single-process mode this allows reusing the Vec and the only cost is
80    /// allocating a new Arc. Prefer over `Self::from_bytes` if ownership is
81    /// transferred.
82    pub fn from_vec(bytes: Vec<u8>) -> Self {
83        if servo_config::opts::get().multiprocess || servo_config::opts::get().force_ipc {
84            GenericSharedMemory(GenericSharedMemoryVariant::Ipc(
85                IpcSharedMemory::from_bytes(&bytes),
86            ))
87        } else {
88            GenericSharedMemory(GenericSharedMemoryVariant::InProcess(Arc::new(bytes)))
89        }
90    }
91
92    /// Build a `GenericSharedMemory` from an `Arc<Vec<u8>>`.
93    ///
94    /// In single-process mode this allows creating shared memory without copying.
95    pub fn from_arc_vec(arc: Arc<Vec<u8>>) -> Self {
96        if servo_config::opts::get().multiprocess || servo_config::opts::get().force_ipc {
97            GenericSharedMemory(GenericSharedMemoryVariant::Ipc(
98                IpcSharedMemory::from_bytes(&arc),
99            ))
100        } else {
101            GenericSharedMemory(GenericSharedMemoryVariant::InProcess(arc))
102        }
103    }
104
105    /// Free operation in single process mode.
106    /// If multiple `GenericSharedmemory` point to the same value this is safe to use and only effects the value currently hold.
107    pub fn into_arc_vec(self) -> Arc<Vec<u8>> {
108        match self.0 {
109            GenericSharedMemoryVariant::Ipc(ipc_shared_memory) => {
110                Arc::new(ipc_shared_memory.to_vec())
111            },
112            GenericSharedMemoryVariant::InProcess(arc) => arc,
113        }
114    }
115
116    pub fn from_bytes_with_mutator(bytes: &[u8], mutator: impl FnOnce(&mut [u8])) -> Self {
117        let mut shared_memory = Self::from_bytes(bytes);
118        match &mut shared_memory.0 {
119            GenericSharedMemoryVariant::Ipc(ipc_shared_memory) => {
120                #[expect(unsafe_code)]
121                unsafe {
122                    mutator(ipc_shared_memory.deref_mut())
123                }
124            },
125            GenericSharedMemoryVariant::InProcess(arc) => mutator(
126                Arc::get_mut(arc)
127                    .expect("Arc just created from bytes")
128                    .as_mut_slice(),
129            ),
130        }
131        shared_memory
132    }
133}
134
135impl fmt::Debug for GenericSharedMemory {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        f.debug_tuple("GenericSharedMemory").finish()
138    }
139}
140
141impl Serialize for GenericSharedMemory {
142    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
143        match &self.0 {
144            GenericSharedMemoryVariant::Ipc(memory) => {
145                s.serialize_newtype_variant("GenericSharedMemory", 0, "Ipc", memory)
146            },
147            GenericSharedMemoryVariant::InProcess(arc) => {
148                if opts::get().multiprocess || opts::get().force_ipc {
149                    return Err(serde::ser::Error::custom(
150                        "Arc<Vec<u8>> found in multiprocess mode!",
151                    ));
152                } // We know everything is in one address-space, so we can "serialize" the receiver by
153                // sending a leaked Arc pointer.
154                let address = Arc::into_raw(arc.clone()) as *mut Vec<u8> as usize;
155                s.serialize_newtype_variant("GenericSharedMemory", 1, "InProcess", &address)
156            },
157        }
158    }
159}
160
161struct GenericSharedMemoryVisitor {}
162
163impl<'de> serde::de::Visitor<'de> for GenericSharedMemoryVisitor {
164    type Value = GenericSharedMemory;
165
166    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
167        formatter.write_str("a GenericReceiver variant")
168    }
169
170    fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
171    where
172        A: serde::de::EnumAccess<'de>,
173    {
174        #[derive(Deserialize)]
175        enum GenericSharedMemoryVariantNames {
176            Ipc,
177            InProcess,
178        }
179
180        let (variant_name, variant_data): (GenericSharedMemoryVariantNames, _) = data.variant()?;
181
182        match variant_name {
183            GenericSharedMemoryVariantNames::Ipc => variant_data
184                .newtype_variant::<IpcSharedMemory>()
185                .map(|receiver| GenericSharedMemory(GenericSharedMemoryVariant::Ipc(receiver))),
186            GenericSharedMemoryVariantNames::InProcess => {
187                if opts::get().multiprocess || servo_config::opts::get().force_ipc {
188                    return Err(serde::de::Error::custom(
189                        "Arc data found in multiprocess mode!",
190                    ));
191                }
192                let addr = variant_data.newtype_variant::<usize>()?;
193                let ptr = addr as *mut Vec<u8>;
194                // SAFETY: We know we are in the same address space as the sender, so we can safely
195                // reconstruct the Arc.
196                #[expect(unsafe_code)]
197                let arc = unsafe { Arc::from_raw(ptr) };
198                Ok(GenericSharedMemory(GenericSharedMemoryVariant::InProcess(
199                    arc,
200                )))
201            },
202        }
203    }
204}
205
206impl<'a> Deserialize<'a> for GenericSharedMemory {
207    fn deserialize<D>(d: D) -> Result<GenericSharedMemory, D::Error>
208    where
209        D: Deserializer<'a>,
210    {
211        d.deserialize_enum(
212            "GenericSharedMemory",
213            &["Ipc", "InProcess"],
214            GenericSharedMemoryVisitor {},
215        )
216    }
217}
218
219#[cfg(test)]
220mod single_process_shared_memory_test {
221    use std::sync::Arc;
222
223    use ipc_channel::ipc::IpcSharedMemory;
224
225    use super::GenericSharedMemory;
226    use crate::generic_channel::{self};
227
228    #[test]
229    fn test_ipc() {
230        let bytes = vec![0xba; 10];
231        let bytes_copy = bytes.clone();
232        let shared_memory = GenericSharedMemory(super::GenericSharedMemoryVariant::Ipc(
233            IpcSharedMemory::from_bytes(&bytes),
234        ));
235
236        let (send, recv) = generic_channel::channel().unwrap();
237        send.send(shared_memory).expect("Could not send");
238        assert_eq!(recv.recv().unwrap().to_vec(), bytes_copy);
239    }
240
241    #[test]
242    fn test_inprocess() {
243        let bytes = vec![0xba; 10];
244        let bytes_copy = bytes.clone();
245        let shared_memory = GenericSharedMemory(super::GenericSharedMemoryVariant::InProcess(
246            Arc::new(bytes.clone()),
247        ));
248
249        let (send, recv) = generic_channel::channel().unwrap();
250        send.send(shared_memory).expect("Could not send");
251        assert_eq!(recv.recv().unwrap().to_vec(), bytes_copy);
252    }
253}