mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Add SharedRwLock<T> and Locked<T>
This commit is contained in:
parent
fbd561bc2f
commit
8feb9e8047
2 changed files with 165 additions and 0 deletions
|
@ -106,6 +106,7 @@ pub mod restyle_hints;
|
||||||
pub mod rule_tree;
|
pub mod rule_tree;
|
||||||
pub mod scoped_tls;
|
pub mod scoped_tls;
|
||||||
pub mod selector_parser;
|
pub mod selector_parser;
|
||||||
|
pub mod shared_lock;
|
||||||
pub mod stylist;
|
pub mod stylist;
|
||||||
#[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo;
|
#[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo;
|
||||||
pub mod sequential;
|
pub mod sequential;
|
||||||
|
@ -168,6 +169,8 @@ macro_rules! reexport_computed_values {
|
||||||
longhand_properties_idents!(reexport_computed_values);
|
longhand_properties_idents!(reexport_computed_values);
|
||||||
|
|
||||||
/// Returns whether the two arguments point to the same value.
|
/// 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]
|
#[inline]
|
||||||
pub fn arc_ptr_eq<T: 'static>(a: &Arc<T>, b: &Arc<T>) -> bool {
|
pub fn arc_ptr_eq<T: 'static>(a: &Arc<T>, b: &Arc<T>) -> bool {
|
||||||
let a: &T = &**a;
|
let a: &T = &**a;
|
||||||
|
|
162
components/style/shared_lock.rs
Normal file
162
components/style/shared_lock.rs
Normal file
|
@ -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<RwLock<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<T>(&self, data: T) -> Locked<T> {
|
||||||
|
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<T> {
|
||||||
|
shared_lock: SharedRwLock,
|
||||||
|
data: UnsafeCell<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe: the data inside `UnsafeCell` is only accessed in `read_with` and `write_with`,
|
||||||
|
// where guards ensure synchronization.
|
||||||
|
unsafe impl<T: Send> Send for Locked<T> {}
|
||||||
|
unsafe impl<T: Send + Sync> Sync for Locked<T> {}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for Locked<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let guard = self.shared_lock.read();
|
||||||
|
self.read_with(&guard).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Locked<T> {
|
||||||
|
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<T: Clone> Marker1 for T {}
|
||||||
|
impl<'a> Marker1 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Clone
|
||||||
|
impl<'a> Marker1 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Clone
|
||||||
|
|
||||||
|
trait Marker2 {}
|
||||||
|
impl<T: Copy> Marker2 for T {}
|
||||||
|
impl<'a> Marker2 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Copy
|
||||||
|
impl<'a> Marker2 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Copy
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue