use crate::str::{CssString, CssStringWriter};
use crate::stylesheets::Origin;
#[cfg(feature = "gecko")]
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
#[cfg(feature = "servo")]
use parking_lot::RwLock;
use servo_arc::Arc;
use std::cell::UnsafeCell;
use std::fmt;
#[cfg(feature = "servo")]
use std::mem;
#[cfg(feature = "gecko")]
use std::ptr;
use to_shmem::{SharedMemoryBuilder, ToShmem};
#[derive(Clone)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub struct SharedRwLock {
#[cfg(feature = "servo")]
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
arc: Arc<RwLock<()>>,
#[cfg(feature = "gecko")]
cell: Option<Arc<AtomicRefCell<SomethingZeroSizedButTyped>>>,
}
#[cfg(feature = "gecko")]
struct SomethingZeroSizedButTyped;
impl fmt::Debug for SharedRwLock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("SharedRwLock")
}
}
impl SharedRwLock {
#[cfg(feature = "servo")]
pub fn new() -> Self {
SharedRwLock {
arc: Arc::new(RwLock::new(())),
}
}
#[cfg(feature = "gecko")]
pub fn new() -> Self {
SharedRwLock {
cell: Some(Arc::new(AtomicRefCell::new(SomethingZeroSizedButTyped))),
}
}
#[cfg(feature = "servo")]
pub fn new_leaked() -> Self {
SharedRwLock {
arc: Arc::new_leaked(RwLock::new(())),
}
}
#[cfg(feature = "gecko")]
pub fn new_leaked() -> Self {
SharedRwLock {
cell: Some(Arc::new_leaked(AtomicRefCell::new(
SomethingZeroSizedButTyped,
))),
}
}
#[cfg(feature = "gecko")]
pub fn read_only() -> Self {
SharedRwLock { cell: None }
}
#[cfg(feature = "gecko")]
#[inline]
fn ptr(&self) -> *const SomethingZeroSizedButTyped {
self.cell
.as_ref()
.map(|cell| cell.as_ptr() as *const _)
.unwrap_or(ptr::null())
}
pub fn wrap<T>(&self, data: T) -> Locked<T> {
Locked {
shared_lock: self.clone(),
data: UnsafeCell::new(data),
}
}
#[cfg(feature = "servo")]
pub fn read(&self) -> SharedRwLockReadGuard {
mem::forget(self.arc.read());
SharedRwLockReadGuard(self)
}
#[cfg(feature = "gecko")]
pub fn read(&self) -> SharedRwLockReadGuard {
SharedRwLockReadGuard(self.cell.as_ref().map(|cell| cell.borrow()))
}
#[cfg(feature = "servo")]
pub fn write(&self) -> SharedRwLockWriteGuard {
mem::forget(self.arc.write());
SharedRwLockWriteGuard(self)
}
#[cfg(feature = "gecko")]
pub fn write(&self) -> SharedRwLockWriteGuard {
SharedRwLockWriteGuard(self.cell.as_ref().unwrap().borrow_mut())
}
}
#[cfg(feature = "servo")]
pub struct SharedRwLockReadGuard<'a>(&'a SharedRwLock);
#[cfg(feature = "gecko")]
pub struct SharedRwLockReadGuard<'a>(Option<AtomicRef<'a, SomethingZeroSizedButTyped>>);
#[cfg(feature = "servo")]
impl<'a> Drop for SharedRwLockReadGuard<'a> {
fn drop(&mut self) {
unsafe { self.0.arc.force_unlock_read() }
}
}
impl<'a> SharedRwLockReadGuard<'a> {
#[inline]
#[cfg(feature = "gecko")]
fn ptr(&self) -> *const SomethingZeroSizedButTyped {
self.0
.as_ref()
.map(|r| &**r as *const _)
.unwrap_or(ptr::null())
}
}
#[cfg(feature = "servo")]
pub struct SharedRwLockWriteGuard<'a>(&'a SharedRwLock);
#[cfg(feature = "gecko")]
pub struct SharedRwLockWriteGuard<'a>(AtomicRefMut<'a, SomethingZeroSizedButTyped>);
#[cfg(feature = "servo")]
impl<'a> Drop for SharedRwLockWriteGuard<'a> {
fn drop(&mut self) {
unsafe { self.0.arc.force_unlock_write() }
}
}
pub struct Locked<T> {
shared_lock: SharedRwLock,
data: UnsafeCell<T>,
}
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> {
#[cfg(feature = "gecko")]
#[inline]
fn is_read_only_lock(&self) -> bool {
self.shared_lock.cell.is_none()
}
#[cfg(feature = "servo")]
fn same_lock_as(&self, lock: &SharedRwLock) -> bool {
Arc::ptr_eq(&self.shared_lock.arc, &lock.arc)
}
#[cfg(feature = "gecko")]
fn same_lock_as(&self, ptr: *const SomethingZeroSizedButTyped) -> bool {
ptr::eq(self.shared_lock.ptr(), ptr)
}
pub fn read_with<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T {
#[cfg(feature = "gecko")]
assert!(
self.is_read_only_lock() || self.same_lock_as(guard.ptr()),
"Locked::read_with called with a guard from an unrelated SharedRwLock: {:?} vs. {:?}",
self.shared_lock.ptr(),
guard.ptr(),
);
#[cfg(not(feature = "gecko"))]
assert!(self.same_lock_as(&guard.0));
let ptr = self.data.get();
unsafe { &*ptr }
}
#[cfg(feature = "gecko")]
pub unsafe fn read_unchecked<'a>(&'a self) -> &'a T {
let ptr = self.data.get();
&*ptr
}
pub fn write_with<'a>(&'a self, guard: &'a mut SharedRwLockWriteGuard) -> &'a mut T {
#[cfg(feature = "gecko")]
assert!(
!self.is_read_only_lock() && self.same_lock_as(&*guard.0),
"Locked::write_with called with a guard from a read only or unrelated SharedRwLock"
);
#[cfg(not(feature = "gecko"))]
assert!(self.same_lock_as(&guard.0));
let ptr = self.data.get();
unsafe { &mut *ptr }
}
}
#[cfg(feature = "gecko")]
impl<T: ToShmem> ToShmem for Locked<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
use std::mem::ManuallyDrop;
let guard = self.shared_lock.read();
Ok(ManuallyDrop::new(Locked {
shared_lock: SharedRwLock::read_only(),
data: UnsafeCell::new(ManuallyDrop::into_inner(
self.read_with(&guard).to_shmem(builder)?,
)),
}))
}
}
#[cfg(feature = "servo")]
impl<T: ToShmem> ToShmem for Locked<T> {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
panic!("ToShmem not supported in Servo currently")
}
}
#[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> {} impl<'a> Marker1 for SharedRwLockWriteGuard<'a> {} trait Marker2 {}
impl<T: Copy> Marker2 for T {}
impl<'a> Marker2 for SharedRwLockReadGuard<'a> {} impl<'a> Marker2 for SharedRwLockWriteGuard<'a> {} }
pub trait ToCssWithGuard {
fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result;
#[inline]
fn to_css_string(&self, guard: &SharedRwLockReadGuard) -> CssString {
let mut s = CssString::new();
self.to_css(guard, &mut s).unwrap();
s
}
}
pub trait DeepCloneWithLock: Sized {
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
) -> Self;
}
#[derive(Clone)]
pub struct StylesheetGuards<'a> {
pub author: &'a SharedRwLockReadGuard<'a>,
pub ua_or_user: &'a SharedRwLockReadGuard<'a>,
}
impl<'a> StylesheetGuards<'a> {
pub fn for_origin(&self, origin: Origin) -> &SharedRwLockReadGuard<'a> {
match origin {
Origin::Author => &self.author,
_ => &self.ua_or_user,
}
}
pub fn same(guard: &'a SharedRwLockReadGuard<'a>) -> Self {
StylesheetGuards {
author: guard,
ua_or_user: guard,
}
}
}