Skip to content

Instantly share code, notes, and snippets.

Created September 19, 2013 01:05
Show Gist options
  • Save anonymous/f007d9b907509c03c17f to your computer and use it in GitHub Desktop.
Save anonymous/f007d9b907509c03c17f to your computer and use it in GitHub Desktop.
#[cfg(test)]
extern mod extra;
use std::cast::transmute_mut;
#[no_freeze]
pub struct Mut<T> {
priv value: T,
priv status: BorrowStatus,
}
#[deriving(Eq)]
enum BorrowStatus {
/// the Reading count increases with each ReadPtr,
/// starting with 0 for the first one so that all values are used.
Reading(uint),
Writing,
Unused,
}
impl<T> Mut<T> {
pub fn new(x: T) -> Mut<T> {
Mut{value: x, status: Unused}
}
pub fn unwrap(self) -> T {
match self.status {
Unused => self.value,
// debug assert?
_ => fail!("borrow inconsistency in Mut<T>"),
}
}
pub fn borrow<'a>(&'a self) -> ReadPtr<'a, T> {
unsafe {
let mut_self = transmute_mut(self);
mut_self.status = match mut_self.status {
Unused => Reading(0),
Reading(x) => Reading(x + 1),
_ => fail!("borrow: Cannot borrow while writing")
};
}
ReadPtr{parent: self}
}
pub fn borrow_mut<'a>(&'a self) -> WritePtr<'a, T> {
unsafe {
let mut_self = transmute_mut(self);
mut_self.status = match mut_self.status {
Unused => Writing,
_ => fail!("borrow_mut: Mut already in use"),
};
WritePtr{parent: mut_self}
}
}
pub fn map<U>(&self, f: &fn(&T) -> U) -> U {
let r_ptr = self.borrow();
f(r_ptr.get())
}
pub fn map_mut<U>(&self, f: &fn(&mut T) -> U) -> U {
let mut m_ptr = self.borrow_mut();
f(m_ptr.get())
}
}
impl<T: Clone> Clone for Mut<T> {
fn clone(&self) -> Mut<T> {
self.map(|x| Mut::new((*x).clone()))
}
}
pub struct ReadPtr<'self, T> {
priv parent: &'self Mut<T>
}
impl<'self, T> ReadPtr<'self, T> {
pub fn get<'a>(&'a self) -> &'a T {
&self.parent.value
}
pub fn release(self) { }
}
#[unsafe_destructor]
impl<'self, T> Drop for ReadPtr<'self, T> {
fn drop(&mut self) {
unsafe {
let mut_par = transmute_mut(self.parent);
match mut_par.status {
Reading(0) => mut_par.status = Unused,
Reading(x) => mut_par.status = Reading(x-1),
// FIXME: should be debug assert?
_ => error!("ReadPtr::drop: borrow inconsistency in Mut<T>")
}
}
}
}
pub struct WritePtr<'self, T> {
priv parent: &'self mut Mut<T>
}
impl<'self, T> WritePtr<'self, T> {
pub fn get<'a>(&'a mut self) -> &'a mut T {
&mut self.parent.value
}
pub fn release(self) { }
}
#[unsafe_destructor]
impl<'self, T> Drop for WritePtr<'self, T> {
fn drop(&mut self) {
unsafe {
let mut_par = transmute_mut(self.parent);
match mut_par.status {
Writing => mut_par.status = Unused,
// FIXME: should debug assert?
_ => error!("WritePtr::drop: borrow inconsistency in Mut<T>")
}
}
}
}
#[test]
fn test_read_then_read() {
let obj = Mut::new(1);
let r = obj.borrow();
let q = obj.borrow();
assert_eq!(r.get(), q.get());
assert_eq!(obj.status, Reading(1));
r.release();
assert_eq!(obj.status, Reading(0));
q.release();
assert_eq!(obj.status, Unused);
}
#[test]
#[should_fail]
fn test_read_release_partial_then_write() {
let obj = Mut::new(1);
let r = obj.borrow();
let q = obj.borrow();
assert_eq!(r.get(), q.get());
r.release();
let _ = obj.borrow_mut();
}
#[test]
#[should_fail]
fn test_write_then_write() {
let obj = Mut::new(1);
let mptr = obj.borrow_mut();
let nptr = obj.borrow_mut();
mptr.release();
nptr.release();
}
#[test]
fn test_read_release_then_write() {
let obj = Mut::new(1);
let r = obj.borrow();
let q = obj.borrow();
assert_eq!(r.get(), q.get());
r.release();
q.release();
let mut m = obj.borrow_mut();
*m.get() = 99;
m.release();
let r = obj.borrow();
assert_eq!(*r.get(), 99);
}
#[test]
#[should_fail]
fn test_read_then_write() {
let obj = Mut::new(1);
let r = obj.borrow();
let _ = obj.borrow_mut();
r.release();
}
#[test]
fn test_inside_opt() {
let obj = Some(Mut::new(1));
let mut r = obj.map(|y| y.borrow_mut());
match r {
Some(ref mut token) => *token.get() = 99,
None => ()
}
std::util::ignore(r);
let token = obj.get_ref().borrow();
assert_eq!(*token.get(), 99);
}
#[test]
fn test_inside_rc() {
use extra::rc::Rc;
let rc = Rc::from_send(Mut::new(1));
let rc2 = rc.clone();
let rtoken = rc2.borrow().borrow();
assert_eq!(*rtoken.get(), 1);
rtoken.release();
do rc2.borrow().map_mut |t| {
*t = 3;
}
assert_eq!(3, rc.borrow().map(|t| *t));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment