diff --git a/components/style/lib.rs b/components/style/lib.rs index b5bc9f74440..58dcafe9ceb 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -106,6 +106,7 @@ pub mod restyle_hints; pub mod rule_tree; pub mod scoped_tls; pub mod selector_parser; +pub mod shared_lock; pub mod stylist; #[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo; pub mod sequential; @@ -168,6 +169,8 @@ macro_rules! reexport_computed_values { longhand_properties_idents!(reexport_computed_values); /// Returns whether the two arguments point to the same value. +/// +/// FIXME: Remove this and use Arc::ptr_eq once we require Rust 1.17 #[inline] pub fn arc_ptr_eq(a: &Arc, b: &Arc) -> bool { let a: &T = &**a; diff --git a/components/style/shared_lock.rs b/components/style/shared_lock.rs new file mode 100644 index 00000000000..4565e6e5f69 --- /dev/null +++ b/components/style/shared_lock.rs @@ -0,0 +1,162 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Different objects protected by the same lock + +use parking_lot::RwLock; +use std::cell::UnsafeCell; +use std::fmt; +use std::sync::Arc; + +/// A shared read/write lock that can protect multiple objects. +#[derive(Clone)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct SharedRwLock { + #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] + arc: Arc>, +} + +impl fmt::Debug for SharedRwLock { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("SharedRwLock") + } +} + +impl SharedRwLock { + /// Create a new shared lock + pub fn new() -> Self { + SharedRwLock { + arc: Arc::new(RwLock::new(())) + } + } + + /// Wrap the given data to make its access protected by this lock. + pub fn wrap(&self, data: T) -> Locked { + Locked { + shared_lock: self.clone(), + data: UnsafeCell::new(data), + } + } + + /// Obtain the lock for reading + pub fn read(&self) -> SharedRwLockReadGuard { + self.arc.raw_read(); + SharedRwLockReadGuard { + shared_lock: self + } + } + + /// Obtain the lock for writing + pub fn write(&self) -> SharedRwLockWriteGuard { + self.arc.raw_write(); + SharedRwLockWriteGuard { + shared_lock: self + } + } +} + +/// Data protect by a shared lock. +pub struct Locked { + shared_lock: SharedRwLock, + data: UnsafeCell, +} + +// Unsafe: the data inside `UnsafeCell` is only accessed in `read_with` and `write_with`, +// where guards ensure synchronization. +unsafe impl Send for Locked {} +unsafe impl Sync for Locked {} + +impl fmt::Debug for Locked { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let guard = self.shared_lock.read(); + self.read_with(&guard).fmt(f) + } +} + +impl Locked { + fn same_lock_as(&self, lock: &SharedRwLock) -> bool { + ::arc_ptr_eq(&self.shared_lock.arc, &lock.arc) + } + + /// Access the data for reading. + pub fn read_with<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T { + assert!(self.same_lock_as(&guard.shared_lock), + "Locked::read_with called with a guard from an unrelated SharedRwLock"); + let ptr = self.data.get(); + + // Unsafe: + // + // * The guard guarantees that the lock is taken for reading, + // and we’ve checked that it’s the correct lock. + // * The returned reference borrows *both* the data and the guard, + // so that it can outlive neither. + unsafe { + &*ptr + } + } + + /// Access the data for writing. + pub fn write_with<'a>(&'a self, guard: &'a mut SharedRwLockWriteGuard) -> &'a mut T { + assert!(self.same_lock_as(&guard.shared_lock), + "Locked::write_with called with a guard from an unrelated SharedRwLock"); + let ptr = self.data.get(); + + // Unsafe: + // + // * The guard guarantees that the lock is taken for writing, + // and we’ve checked that it’s the correct lock. + // * The returned reference borrows *both* the data and the guard, + // so that it can outlive neither. + // * We require a mutable borrow of the guard, + // so that one write guard can only be used once at a time. + unsafe { + &mut *ptr + } + } +} + +/// Proof that a shared lock was obtained for reading. +pub struct SharedRwLockReadGuard<'a> { + shared_lock: &'a SharedRwLock, +} + +/// Proof that a shared lock was obtained for writing. +pub struct SharedRwLockWriteGuard<'a> { + shared_lock: &'a SharedRwLock, +} + +impl<'a> Drop for SharedRwLockReadGuard<'a> { + fn drop(&mut self) { + // Unsafe: self.lock is private to this module, only ever set after `raw_read()`, + // and never copied or cloned (see `compile_time_assert` below). + unsafe { + self.shared_lock.arc.raw_unlock_read() + } + } +} + +impl<'a> Drop for SharedRwLockWriteGuard<'a> { + fn drop(&mut self) { + // Unsafe: self.lock is private to this module, only ever set after `raw_write()`, + // and never copied or cloned (see `compile_time_assert` below). + unsafe { + self.shared_lock.arc.raw_unlock_write() + } + } +} + +#[allow(dead_code)] +mod compile_time_assert { + use super::{SharedRwLockReadGuard, SharedRwLockWriteGuard}; + + trait Marker1 {} + impl Marker1 for T {} + impl<'a> Marker1 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Clone + impl<'a> Marker1 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Clone + + trait Marker2 {} + impl Marker2 for T {} + impl<'a> Marker2 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Copy + impl<'a> Marker2 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Copy +}