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.

Revisions

  1. @invalid-email-address Anonymous created this gist Sep 19, 2013.
    212 changes: 212 additions & 0 deletions gistfile1.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,212 @@
    #[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));
    }