// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /** Task-local reference counted smart pointers Task-local reference counted smart pointers are an alternative to managed boxes with deterministic destruction. They are restricted to containing `Owned` types in order to prevent cycles. */ use core::libc::{c_void, size_t, malloc, free}; use core::unstable::intrinsics; struct RcBox { value: T, count: uint } /// Immutable reference counted pointer type #[non_owned] pub struct Rc { priv ptr: *mut RcBox, } pub impl<'self, T: Owned> Rc { fn new(value: T) -> Rc { unsafe { let ptr = malloc(sys::size_of::>() as size_t) as *mut RcBox; assert!(!ptr::is_null(ptr)); intrinsics::move_val_init(&mut *ptr, RcBox{value: value, count: 1}); Rc{ptr: ptr} } } #[inline(always)] fn borrow(&self) -> &'self T { unsafe { cast::transmute_region(&(*self.ptr).value) } } } #[unsafe_destructor] impl Drop for Rc { fn finalize(&self) { unsafe { (*self.ptr).count -= 1; if (*self.ptr).count == 0 { let mut x = intrinsics::init(); x <-> *self.ptr; free(self.ptr as *c_void) } } } } impl Clone for Rc { #[inline] fn clone(&self) -> Rc { unsafe { (*self.ptr).count += 1; Rc{ptr: self.ptr} } } } #[cfg(test)] mod test_rc { use super::*; #[test] fn test_simple() { let x = Rc::new(5); assert_eq!(*x.borrow(), 5); } #[test] fn test_clone() { let x = Rc::new(5); let y = x.clone(); assert_eq!(*x.borrow(), 5); assert_eq!(*y.borrow(), 5); } #[test] fn test_destructor() { let x = Rc::new(~5); assert_eq!(**x.borrow(), 5); } } #[abi = "rust-intrinsic"] extern "rust-intrinsic" mod rusti { fn init() -> T; } #[deriving(Eq)] enum Borrow { Mutable, Immutable, Nothing } struct RcMutBox { value: T, count: uint, borrow: Borrow } /// Mutable reference counted pointer type #[non_owned] #[mutable] pub struct RcMut { priv ptr: *mut RcMutBox, } pub impl<'self, T: Owned> RcMut { fn new(value: T) -> RcMut { unsafe { let ptr = malloc(sys::size_of::>() as size_t) as *mut RcMutBox; assert!(!ptr::is_null(ptr)); intrinsics::move_val_init(&mut *ptr, RcMutBox{value: value, count: 1, borrow: Nothing}); RcMut{ptr: ptr} } } /// Fails if there is already a mutable borrow of the box #[inline] fn with_borrow(&self, f: &fn(&T)) { unsafe { assert!((*self.ptr).borrow != Mutable); let previous = (*self.ptr).borrow; (*self.ptr).borrow = Immutable; f(cast::transmute_region(&(*self.ptr).value)); (*self.ptr).borrow = previous; } } /// Fails if there is already a mutable or immutable borrow of the box #[inline] fn with_mut_borrow(&self, f: &fn(&mut T)) { unsafe { assert!((*self.ptr).borrow == Nothing); (*self.ptr).borrow = Mutable; f(cast::transmute_mut_region(&mut (*self.ptr).value)); (*self.ptr).borrow = Nothing; } } } #[unsafe_destructor] impl Drop for RcMut { fn finalize(&self) { unsafe { (*self.ptr).count -= 1; if (*self.ptr).count == 0 { let mut x = rusti::init(); x <-> *self.ptr; free(self.ptr as *c_void) } } } } impl Clone for RcMut { #[inline] fn clone(&self) -> RcMut { unsafe { (*self.ptr).count += 1; RcMut{ptr: self.ptr} } } } #[cfg(test)] mod test_rc_mut { use super::*; #[test] fn borrow_many() { let x = RcMut::new(5); let y = x.clone(); do x.with_borrow |a| { assert_eq!(*a, 5); do y.with_borrow |b| { assert_eq!(*b, 5); do x.with_borrow |c| { assert_eq!(*c, 5); } } } } #[test] fn modify() { let x = RcMut::new(5); let y = x.clone(); do y.with_mut_borrow |a| { assert_eq!(*a, 5); *a = 6; } do x.with_borrow |a| { assert_eq!(*a, 6); } } #[test] fn release_immutable() { let x = RcMut::new(5); do x.with_borrow |_| {} do x.with_mut_borrow |_| {} } #[test] fn release_mutable() { let x = RcMut::new(5); do x.with_mut_borrow |_| {} do x.with_borrow |_| {} } #[test] #[should_fail] fn frozen() { let x = RcMut::new(5); let y = x.clone(); do x.with_borrow |_| { do y.with_mut_borrow |_| { } } } #[test] #[should_fail] fn mutable_dupe() { let x = RcMut::new(5); let y = x.clone(); do x.with_mut_borrow |_| { do y.with_mut_borrow |_| { } } } #[test] #[should_fail] fn mutable_freeze() { let x = RcMut::new(5); let y = x.clone(); do x.with_mut_borrow |_| { do y.with_borrow |_| { } } } #[test] #[should_fail] fn restore_freeze() { let x = RcMut::new(5); let y = x.clone(); do x.with_borrow |_| { do x.with_borrow |_| {} do y.with_mut_borrow |_| {} } } }