diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index c7cdb277247..ce3fb71ef2c 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -65,6 +65,9 @@ pub mod logging; #[cfg(test)] pub mod test; +/// Reference counting +pub mod rc; + /// Set up a default runtime configuration, given compiler-supplied arguments. /// /// This is invoked by the `start` _language item_ (unstable::lang) to diff --git a/src/libcore/rt/rc.rs b/src/libcore/rt/rc.rs new file mode 100644 index 00000000000..2ee254466e4 --- /dev/null +++ b/src/libcore/rt/rc.rs @@ -0,0 +1,143 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An owned, task-local, reference counted type +//! +//! # Safety note +//! +//! XXX There is currently no type-system mechanism for enforcing that +//! reference counted types are both allocated on the exchange heap +//! and also non-sendable +//! +//! This doesn't prevent borrowing multiple aliasable mutable pointers + +use ops::Drop; +use clone::Clone; +use libc::c_void; +use sys; +use cast; + +pub struct RC<T> { + p: *c_void // ~(uint, T) +} + +impl<T> RC<T> { + pub fn new(val: T) -> RC<T> { + unsafe { + let v = ~(1, val); + let p: *c_void = cast::transmute(v); + RC { p: p } + } + } + + fn get_mut_state(&mut self) -> *mut (uint, T) { + unsafe { + let p: &mut ~(uint, T) = cast::transmute(&mut self.p); + let p: *mut (uint, T) = &mut **p; + return p; + } + } + + fn get_state(&self) -> *(uint, T) { + unsafe { + let p: &~(uint, T) = cast::transmute(&self.p); + let p: *(uint, T) = &**p; + return p; + } + } + + pub fn unsafe_borrow_mut(&mut self) -> *mut T { + unsafe { + match *self.get_mut_state() { + (_, ref mut p) => { + let p: *mut T = p; + return p; + } + } + } + } + + pub fn refcount(&self) -> uint { + unsafe { + match *self.get_state() { + (count, _) => count + } + } + } +} + +#[unsafe_destructor] +impl<T> Drop for RC<T> { + fn finalize(&self) { + assert!(self.refcount() > 0); + + unsafe { + // XXX: Mutable finalizer + let this: &mut RC<T> = cast::transmute_mut(self); + + match *this.get_mut_state() { + (ref mut count, _) => { + *count = *count - 1 + } + } + + if this.refcount() == 0 { + let _: ~(uint, T) = cast::transmute(this.p); + } + } + } +} + +impl<T> Clone for RC<T> { + fn clone(&self) -> RC<T> { + unsafe { + // XXX: Mutable clone + let this: &mut RC<T> = cast::transmute_mut(self); + + match *this.get_mut_state() { + (ref mut count, _) => { + *count = *count + 1; + } + } + } + + RC { p: self.p } + } +} + +#[cfg(test)] +mod test { + use super::RC; + + #[test] + fn smoke_test() { + unsafe { + let mut v1 = RC::new(100); + assert!(*v1.unsafe_borrow_mut() == 100); + assert!(v1.refcount() == 1); + + let mut v2 = v1.clone(); + assert!(*v2.unsafe_borrow_mut() == 100); + assert!(v2.refcount() == 2); + + *v2.unsafe_borrow_mut() = 200; + assert!(*v2.unsafe_borrow_mut() == 200); + assert!(*v1.unsafe_borrow_mut() == 200); + + let v3 = v2.clone(); + assert!(v3.refcount() == 3); + { + let _v1 = v1; + let _v2 = v2; + } + assert!(v3.refcount() == 1); + } + } +}