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