style/
shared_lock.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
5//! Different objects protected by the same lock
6
7use crate::str::{CssString, CssStringWriter};
8use crate::stylesheets::Origin;
9#[cfg(feature = "gecko")]
10use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
11#[cfg(feature = "servo")]
12use parking_lot::RwLock;
13use servo_arc::Arc;
14use std::cell::UnsafeCell;
15use std::fmt;
16#[cfg(feature = "servo")]
17use std::mem;
18#[cfg(feature = "gecko")]
19use std::ptr;
20use to_shmem::{SharedMemoryBuilder, ToShmem};
21
22/// A shared read/write lock that can protect multiple objects.
23///
24/// In Gecko builds, we don't need the blocking behavior, just the safety. As
25/// such we implement this with an AtomicRefCell instead in Gecko builds,
26/// which is ~2x as fast, and panics (rather than deadlocking) when things go
27/// wrong (which is much easier to debug on CI).
28///
29/// Servo needs the blocking behavior for its unsynchronized animation setup,
30/// but that may not be web-compatible and may need to be changed (at which
31/// point Servo could use AtomicRefCell too).
32///
33/// Gecko also needs the ability to have "read only" SharedRwLocks, which are
34/// used for objects stored in (read only) shared memory. Attempting to acquire
35/// write access to objects protected by a read only SharedRwLock will panic.
36#[derive(Clone)]
37#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
38pub struct SharedRwLock {
39    #[cfg(feature = "servo")]
40    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
41    arc: Arc<RwLock<()>>,
42
43    #[cfg(feature = "gecko")]
44    cell: Option<Arc<AtomicRefCell<SomethingZeroSizedButTyped>>>,
45}
46
47#[cfg(feature = "gecko")]
48struct SomethingZeroSizedButTyped;
49
50impl fmt::Debug for SharedRwLock {
51    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52        f.write_str("SharedRwLock")
53    }
54}
55
56impl SharedRwLock {
57    /// Create a new shared lock (servo).
58    #[cfg(feature = "servo")]
59    pub fn new() -> Self {
60        SharedRwLock {
61            arc: Arc::new(RwLock::new(())),
62        }
63    }
64
65    /// Create a new shared lock (gecko).
66    #[cfg(feature = "gecko")]
67    pub fn new() -> Self {
68        SharedRwLock {
69            cell: Some(Arc::new(AtomicRefCell::new(SomethingZeroSizedButTyped))),
70        }
71    }
72
73    /// Create a new global shared lock (servo).
74    #[cfg(feature = "servo")]
75    pub fn new_leaked() -> Self {
76        SharedRwLock {
77            arc: Arc::new_leaked(RwLock::new(())),
78        }
79    }
80
81    /// Create a new global shared lock (gecko).
82    #[cfg(feature = "gecko")]
83    pub fn new_leaked() -> Self {
84        SharedRwLock {
85            cell: Some(Arc::new_leaked(AtomicRefCell::new(
86                SomethingZeroSizedButTyped,
87            ))),
88        }
89    }
90
91    /// Create a new read-only shared lock (gecko).
92    #[cfg(feature = "gecko")]
93    pub fn read_only() -> Self {
94        SharedRwLock { cell: None }
95    }
96
97    #[cfg(feature = "gecko")]
98    #[inline]
99    fn ptr(&self) -> *const SomethingZeroSizedButTyped {
100        self.cell
101            .as_ref()
102            .map(|cell| cell.as_ptr() as *const _)
103            .unwrap_or(ptr::null())
104    }
105
106    /// Wrap the given data to make its access protected by this lock.
107    pub fn wrap<T>(&self, data: T) -> Locked<T> {
108        Locked {
109            shared_lock: self.clone(),
110            data: UnsafeCell::new(data),
111        }
112    }
113
114    /// Obtain the lock for reading (servo).
115    #[cfg(feature = "servo")]
116    pub fn read(&self) -> SharedRwLockReadGuard {
117        mem::forget(self.arc.read());
118        SharedRwLockReadGuard(self)
119    }
120
121    /// Obtain the lock for reading (gecko).
122    #[cfg(feature = "gecko")]
123    pub fn read(&self) -> SharedRwLockReadGuard {
124        SharedRwLockReadGuard(self.cell.as_ref().map(|cell| cell.borrow()))
125    }
126
127    /// Obtain the lock for writing (servo).
128    #[cfg(feature = "servo")]
129    pub fn write(&self) -> SharedRwLockWriteGuard {
130        mem::forget(self.arc.write());
131        SharedRwLockWriteGuard(self)
132    }
133
134    /// Obtain the lock for writing (gecko).
135    #[cfg(feature = "gecko")]
136    pub fn write(&self) -> SharedRwLockWriteGuard {
137        SharedRwLockWriteGuard(self.cell.as_ref().unwrap().borrow_mut())
138    }
139}
140
141/// Proof that a shared lock was obtained for reading (servo).
142#[cfg(feature = "servo")]
143pub struct SharedRwLockReadGuard<'a>(&'a SharedRwLock);
144/// Proof that a shared lock was obtained for reading (gecko).
145#[cfg(feature = "gecko")]
146pub struct SharedRwLockReadGuard<'a>(Option<AtomicRef<'a, SomethingZeroSizedButTyped>>);
147#[cfg(feature = "servo")]
148impl<'a> Drop for SharedRwLockReadGuard<'a> {
149    fn drop(&mut self) {
150        // Unsafe: self.lock is private to this module, only ever set after `read()`,
151        // and never copied or cloned (see `compile_time_assert` below).
152        unsafe { self.0.arc.force_unlock_read() }
153    }
154}
155
156impl<'a> SharedRwLockReadGuard<'a> {
157    #[inline]
158    #[cfg(feature = "gecko")]
159    fn ptr(&self) -> *const SomethingZeroSizedButTyped {
160        self.0
161            .as_ref()
162            .map(|r| &**r as *const _)
163            .unwrap_or(ptr::null())
164    }
165}
166
167/// Proof that a shared lock was obtained for writing (servo).
168#[cfg(feature = "servo")]
169pub struct SharedRwLockWriteGuard<'a>(&'a SharedRwLock);
170/// Proof that a shared lock was obtained for writing (gecko).
171#[cfg(feature = "gecko")]
172pub struct SharedRwLockWriteGuard<'a>(AtomicRefMut<'a, SomethingZeroSizedButTyped>);
173#[cfg(feature = "servo")]
174impl<'a> Drop for SharedRwLockWriteGuard<'a> {
175    fn drop(&mut self) {
176        // Unsafe: self.lock is private to this module, only ever set after `write()`,
177        // and never copied or cloned (see `compile_time_assert` below).
178        unsafe { self.0.arc.force_unlock_write() }
179    }
180}
181
182/// Data protect by a shared lock.
183pub struct Locked<T> {
184    shared_lock: SharedRwLock,
185    data: UnsafeCell<T>,
186}
187
188// Unsafe: the data inside `UnsafeCell` is only accessed in `read_with` and `write_with`,
189// where guards ensure synchronization.
190unsafe impl<T: Send> Send for Locked<T> {}
191unsafe impl<T: Send + Sync> Sync for Locked<T> {}
192
193impl<T: fmt::Debug> fmt::Debug for Locked<T> {
194    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195        let guard = self.shared_lock.read();
196        self.read_with(&guard).fmt(f)
197    }
198}
199
200impl<T> Locked<T> {
201    #[cfg(feature = "gecko")]
202    #[inline]
203    fn is_read_only_lock(&self) -> bool {
204        self.shared_lock.cell.is_none()
205    }
206
207    #[cfg(feature = "servo")]
208    fn same_lock_as(&self, lock: &SharedRwLock) -> bool {
209        Arc::ptr_eq(&self.shared_lock.arc, &lock.arc)
210    }
211
212    #[cfg(feature = "gecko")]
213    fn same_lock_as(&self, ptr: *const SomethingZeroSizedButTyped) -> bool {
214        ptr::eq(self.shared_lock.ptr(), ptr)
215    }
216
217    /// Access the data for reading.
218    pub fn read_with<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T {
219        #[cfg(feature = "gecko")]
220        assert!(
221            self.is_read_only_lock() || self.same_lock_as(guard.ptr()),
222            "Locked::read_with called with a guard from an unrelated SharedRwLock: {:?} vs. {:?}",
223            self.shared_lock.ptr(),
224            guard.ptr(),
225        );
226        #[cfg(not(feature = "gecko"))]
227        assert!(self.same_lock_as(&guard.0));
228
229        let ptr = self.data.get();
230
231        // Unsafe:
232        //
233        // * The guard guarantees that the lock is taken for reading,
234        //   and we’ve checked that it’s the correct lock.
235        // * The returned reference borrows *both* the data and the guard,
236        //   so that it can outlive neither.
237        unsafe { &*ptr }
238    }
239
240    /// Access the data for reading without verifying the lock. Use with caution.
241    #[cfg(feature = "gecko")]
242    pub unsafe fn read_unchecked<'a>(&'a self) -> &'a T {
243        let ptr = self.data.get();
244        &*ptr
245    }
246
247    /// Access the data for writing.
248    pub fn write_with<'a>(&'a self, guard: &'a mut SharedRwLockWriteGuard) -> &'a mut T {
249        #[cfg(feature = "gecko")]
250        assert!(
251            !self.is_read_only_lock() && self.same_lock_as(&*guard.0),
252            "Locked::write_with called with a guard from a read only or unrelated SharedRwLock"
253        );
254        #[cfg(not(feature = "gecko"))]
255        assert!(self.same_lock_as(&guard.0));
256
257        let ptr = self.data.get();
258
259        // Unsafe:
260        //
261        // * The guard guarantees that the lock is taken for writing,
262        //   and we’ve checked that it’s the correct lock.
263        // * The returned reference borrows *both* the data and the guard,
264        //   so that it can outlive neither.
265        // * We require a mutable borrow of the guard,
266        //   so that one write guard can only be used once at a time.
267        unsafe { &mut *ptr }
268    }
269}
270
271#[cfg(feature = "gecko")]
272impl<T: ToShmem> ToShmem for Locked<T> {
273    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
274        use std::mem::ManuallyDrop;
275
276        let guard = self.shared_lock.read();
277        Ok(ManuallyDrop::new(Locked {
278            shared_lock: SharedRwLock::read_only(),
279            data: UnsafeCell::new(ManuallyDrop::into_inner(
280                self.read_with(&guard).to_shmem(builder)?,
281            )),
282        }))
283    }
284}
285
286#[cfg(feature = "servo")]
287impl<T: ToShmem> ToShmem for Locked<T> {
288    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
289        panic!("ToShmem not supported in Servo currently")
290    }
291}
292
293#[allow(dead_code)]
294mod compile_time_assert {
295    use super::{SharedRwLockReadGuard, SharedRwLockWriteGuard};
296
297    trait Marker1 {}
298    impl<T: Clone> Marker1 for T {}
299    impl<'a> Marker1 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Clone
300    impl<'a> Marker1 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Clone
301
302    trait Marker2 {}
303    impl<T: Copy> Marker2 for T {}
304    impl<'a> Marker2 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Copy
305    impl<'a> Marker2 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Copy
306}
307
308/// Like ToCss, but with a lock guard given by the caller, and with the writer specified
309/// concretely rather than with a parameter.
310pub trait ToCssWithGuard {
311    /// Serialize `self` in CSS syntax, writing to `dest`, using the given lock guard.
312    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result;
313
314    /// Serialize `self` in CSS syntax using the given lock guard and return a string.
315    ///
316    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
317    #[inline]
318    fn to_css_string(&self, guard: &SharedRwLockReadGuard) -> CssString {
319        let mut s = CssString::new();
320        self.to_css(guard, &mut s).unwrap();
321        s
322    }
323}
324
325/// A trait to do a deep clone of a given CSS type. Gets a lock and a read
326/// guard, in order to be able to read and clone nested structures.
327pub trait DeepCloneWithLock: Sized {
328    /// Deep clones this object.
329    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self;
330}
331
332/// Guards for a document
333#[derive(Clone)]
334pub struct StylesheetGuards<'a> {
335    /// For author-origin stylesheets.
336    pub author: &'a SharedRwLockReadGuard<'a>,
337
338    /// For user-agent-origin and user-origin stylesheets
339    pub ua_or_user: &'a SharedRwLockReadGuard<'a>,
340}
341
342impl<'a> StylesheetGuards<'a> {
343    /// Get the guard for a given stylesheet origin.
344    pub fn for_origin(&self, origin: Origin) -> &SharedRwLockReadGuard<'a> {
345        match origin {
346            Origin::Author => &self.author,
347            _ => &self.ua_or_user,
348        }
349    }
350
351    /// Same guard for all origins
352    pub fn same(guard: &'a SharedRwLockReadGuard<'a>) -> Self {
353        StylesheetGuards {
354            author: guard,
355            ua_or_user: guard,
356        }
357    }
358}