tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

shared_lock.rs (12003B)


      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 
      7 use crate::stylesheets::Origin;
      8 #[cfg(feature = "gecko")]
      9 use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
     10 #[cfg(feature = "servo")]
     11 use parking_lot::RwLock;
     12 use servo_arc::Arc;
     13 use std::cell::UnsafeCell;
     14 use std::fmt;
     15 #[cfg(feature = "servo")]
     16 use std::mem;
     17 #[cfg(feature = "gecko")]
     18 use std::ptr;
     19 use style_traits::{CssString, CssStringWriter};
     20 use 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(crate::derives::MallocSizeOf))]
     38 pub 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")]
     48 struct SomethingZeroSizedButTyped;
     49 
     50 impl fmt::Debug for SharedRwLock {
     51    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     52        f.write_str("SharedRwLock")
     53    }
     54 }
     55 
     56 impl 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")]
    143 pub struct SharedRwLockReadGuard<'a>(&'a SharedRwLock);
    144 /// Proof that a shared lock was obtained for reading (gecko).
    145 #[cfg(feature = "gecko")]
    146 pub struct SharedRwLockReadGuard<'a>(Option<AtomicRef<'a, SomethingZeroSizedButTyped>>);
    147 #[cfg(feature = "servo")]
    148 impl<'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 
    156 impl<'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")]
    169 pub struct SharedRwLockWriteGuard<'a>(&'a SharedRwLock);
    170 /// Proof that a shared lock was obtained for writing (gecko).
    171 #[cfg(feature = "gecko")]
    172 pub struct SharedRwLockWriteGuard<'a>(AtomicRefMut<'a, SomethingZeroSizedButTyped>);
    173 #[cfg(feature = "servo")]
    174 impl<'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.
    183 pub 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.
    190 unsafe impl<T: Send> Send for Locked<T> {}
    191 unsafe impl<T: Send + Sync> Sync for Locked<T> {}
    192 
    193 impl<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 
    200 impl<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")]
    272 impl<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")]
    287 impl<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)]
    294 mod 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.
    310 pub 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.
    327 pub 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)]
    334 pub 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 
    342 impl<'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 }