Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug-only dynamic checks for layout and GC use of DOMRefCell #3797

Merged
merged 6 commits into from Oct 25, 2014
Next

Customize RefCell instead of wrapping it

This gets rid of a dubious transmute:

    let val = mem::transmute::<&RefCell<T>, &T>(&self.base);

The code duplication will be reduced once rust-lang/rust#18131 is fixed.
  • Loading branch information
kmcallister committed Oct 24, 2014
commit 96e180a22c4aa9c4d6c2ed5f3237653ac845fd92
@@ -5,64 +5,142 @@
use dom::bindings::trace::JSTraceable;
use js::jsapi::{JSTracer};

use std::cell;
use std::cell::RefCell;
use std::mem;

/// A mutable field in DOM for large sized value.
/// This has a special method to return the pointer of itself
/// for used in layout task.
/// This simply wraps `RefCell<T>` to add the special method.
use std::cell::{Cell, UnsafeCell};
use std::kinds::marker;

/// A mutable field in the DOM.
///
/// This extends the API of `core::cell::RefCell` to allow unsafe access in
/// certain situations.
pub struct DOMRefCell<T> {
base: RefCell<T>,
value: UnsafeCell<T>,
borrow: Cell<BorrowFlag>,
nocopy: marker::NoCopy,
nosync: marker::NoSync,
}

pub type Ref<'a, T> = cell::Ref<'a, T>;
pub type RefMut<'a, T> = cell::RefMut<'a, T>;
// Functionality specific to Servo's `DOMRefCell` type
// ===================================================

impl<T> DOMRefCell<T> {
/// Return a reference to the contents.
///
/// For use in the layout task only.
pub unsafe fn borrow_for_layout<'a>(&'a self) -> &'a T {
&*self.value.get()
}
}

impl<T: JSTraceable> JSTraceable for DOMRefCell<T> {
fn trace(&self, trc: *mut JSTracer) {
(*self).borrow().trace(trc)
}
}

// Functionality duplicated with `core::cell::RefCell`
// ===================================================
//
// This can shrink once rust-lang/rust#18131 is fixed.

// Values [1, MAX-1] represent the number of `Ref` active
// (will not outgrow its range since `uint` is the size of the address space)
type BorrowFlag = uint;
static UNUSED: BorrowFlag = 0;
static WRITING: BorrowFlag = -1;

impl<T> DOMRefCell<T> {
#[inline(always)]
pub fn new(value: T) -> DOMRefCell<T> {
DOMRefCell {
base: RefCell::new(value),
value: UnsafeCell::new(value),
borrow: Cell::new(UNUSED),
nocopy: marker::NoCopy,
nosync: marker::NoSync,
}
}

#[inline(always)]
pub fn unwrap(self) -> T {
self.base.unwrap()
debug_assert!(self.borrow.get() == UNUSED);
unsafe{self.value.unwrap()}
}

#[inline(always)]
pub fn try_borrow<'a>(&'a self) -> Option<Ref<'a, T>> {
self.base.try_borrow()
match self.borrow.get() {
WRITING => None,
borrow => {
self.borrow.set(borrow + 1);
Some(Ref { _parent: self })
}
}
}

#[inline(always)]
pub fn borrow<'a>(&'a self) -> Ref<'a, T> {
self.base.borrow()
match self.try_borrow() {
Some(ptr) => ptr,
None => fail!("DOMRefCell<T> already mutably borrowed")
}
}

#[inline(always)]
pub fn try_borrow_mut<'a>(&'a self) -> Option<RefMut<'a, T>> {
self.base.try_borrow_mut()
match self.borrow.get() {
UNUSED => {
self.borrow.set(WRITING);
Some(RefMut { _parent: self })
},
_ => None
}
}

#[inline(always)]
pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> {
self.base.borrow_mut()
match self.try_borrow_mut() {
Some(ptr) => ptr,
None => fail!("DOMRefCell<T> already borrowed")
}
}
}

/// This returns the pointer which refers T in `RefCell<T>` directly.
pub unsafe fn borrow_for_layout<'a>(&'a self) -> &'a T {
let val = mem::transmute::<&RefCell<T>, &T>(&self.base);
val
pub struct Ref<'b, T:'b> {
_parent: &'b DOMRefCell<T>
}

#[unsafe_destructor]
impl<'b, T> Drop for Ref<'b, T> {
fn drop(&mut self) {
let borrow = self._parent.borrow.get();
debug_assert!(borrow != WRITING && borrow != UNUSED);
self._parent.borrow.set(borrow - 1);
}
}

impl<T: JSTraceable> JSTraceable for DOMRefCell<T> {
fn trace(&self, trc: *mut JSTracer) {
(*self).base.borrow().trace(trc)
impl<'b, T> Deref<T> for Ref<'b, T> {
#[inline]
fn deref<'a>(&'a self) -> &'a T {
unsafe { &*self._parent.value.get() }
}
}

pub struct RefMut<'b, T:'b> {
_parent: &'b DOMRefCell<T>
}

#[unsafe_destructor]
impl<'b, T> Drop for RefMut<'b, T> {
fn drop(&mut self) {
let borrow = self._parent.borrow.get();
debug_assert!(borrow == WRITING);
self._parent.borrow.set(UNUSED);
}
}

impl<'b, T> Deref<T> for RefMut<'b, T> {
#[inline]
fn deref<'a>(&'a self) -> &'a T {
unsafe { &*self._parent.value.get() }
}
}

impl<'b, T> DerefMut<T> for RefMut<'b, T> {
#[inline]
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
unsafe { &mut *self._parent.value.get() }
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.