Skip to content

Commit

Permalink
Customize RefCell instead of wrapping it
Browse files Browse the repository at this point in the history
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
1 parent 06f322a commit 96e180a
Showing 1 changed file with 108 additions and 30 deletions.
138 changes: 108 additions & 30 deletions components/script/dom/bindings/cell.rs
Expand Up @@ -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() }
}
}

0 comments on commit 96e180a

Please sign in to comment.