-
-
Save anonymous/f007d9b907509c03c17f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[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