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