Skip to content

Commit

Permalink
auto merge of #9794 : thestinger/rust/rc, r=alexcrichton
Browse files Browse the repository at this point in the history
I've left out a way to construct from a `Send` type until #9509 is resolved. I am confident that this interface can remain backwards compatible though, assuming we name the `Pointer` trait method `borrow`.

When there is a way to convert from `Send` (`from_send`), a future RAII-based `Mut` type can be used with this to implemented a mutable reference-counted pointer. For now, I've left around the `RcMut` type but it may drastically change or be removed.
  • Loading branch information
bors committed Oct 11, 2013
2 parents ed37b00 + 18be986 commit c147ec6
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 89 deletions.
1 change: 0 additions & 1 deletion src/libextra/extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ pub use std::os;

pub mod c_vec;
pub mod io_util;
pub mod rc;

// Concurrency

Expand Down
170 changes: 89 additions & 81 deletions src/libextra/rc.rs → src/libstd/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,23 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[allow(missing_doc)];
/** Task-local reference counted boxes
/** Task-local reference counted smart pointers
The `Rc` type provides shared ownership of an immutable value. Destruction is deterministic, and
will occur as soon as the last owner is gone. It is marked as non-sendable because it avoids the
overhead of atomic reference counting.
Task-local reference counted smart pointers are an alternative to managed boxes with deterministic
destruction. They are restricted to containing types that are either `Send` or `Freeze` (or both) to
prevent cycles.
Neither `Rc<T>` or `RcMut<T>` is ever `Send` and `RcMut<T>` is never `Freeze`. If `T` is `Freeze`, a
cycle cannot be created with `Rc<T>` because there is no way to modify it after creation.
The `RcMut` type provides shared ownership of a mutable value. Since multiple owners prevent
inherited mutability, a dynamic freezing check is used to maintain the invariant that an `&mut`
reference is a unique handle and the type is marked as non-`Freeze`.
*/


use std::cast;
use std::ptr;
use std::unstable::intrinsics;

// Convert ~T into *mut T without dropping it
#[inline]
unsafe fn owned_to_raw<T>(mut box: ~T) -> *mut T {
let ptr = ptr::to_mut_unsafe_ptr(box);
intrinsics::forget(box);
ptr
}
use ptr::RawPtr;
use unstable::intrinsics::transmute;
use ops::Drop;
use kinds::{Freeze, Send};
use clone::{Clone, DeepClone};

struct RcBox<T> {
value: T,
Expand All @@ -43,50 +35,38 @@ struct RcBox<T> {
#[unsafe_no_drop_flag]
#[no_send]
pub struct Rc<T> {
priv ptr: *mut RcBox<T>,
}

impl<T> Rc<T> {
unsafe fn new(value: T) -> Rc<T> {
Rc{ptr: owned_to_raw(~RcBox{value: value, count: 1})}
}
}

impl<T: Send> Rc<T> {
pub fn from_send(value: T) -> Rc<T> {
unsafe { Rc::new(value) }
}
priv ptr: *mut RcBox<T>
}

impl<T: Freeze> Rc<T> {
pub fn from_freeze(value: T) -> Rc<T> {
unsafe { Rc::new(value) }
/// Construct a new reference-counted box from a `Freeze` value
#[inline]
pub fn new(value: T) -> Rc<T> {
unsafe {
Rc::new_unchecked(value)
}
}
}

impl<T> Rc<T> {
/// Unsafety construct a new reference-counted box from any value.
///
/// If the type is not `Freeze`, the `Rc` box will incorrectly still be considered as a `Freeze`
/// type. It is also possible to create cycles, which will leak, and may interact poorly with
/// managed pointers.
#[inline]
pub fn borrow<'r>(&'r self) -> &'r T {
unsafe { cast::copy_lifetime(self, &(*self.ptr).value) }
pub unsafe fn new_unchecked(value: T) -> Rc<T> {
Rc{ptr: transmute(~RcBox{value: value, count: 1})}
}
}

#[unsafe_destructor]
impl<T> Drop for Rc<T> {
fn drop(&mut self) {
unsafe {
if self.ptr.is_not_null() {
(*self.ptr).count -= 1;
if (*self.ptr).count == 0 {
let _: ~T = cast::transmute(self.ptr);
}
}
}
/// Borrow the value contained in the reference-counted box
#[inline]
pub fn borrow<'r>(&'r self) -> &'r T {
unsafe { &(*self.ptr).value }
}
}

impl<T> Clone for Rc<T> {
/// Return a shallow copy of the reference counted pointer.
#[inline]
fn clone(&self) -> Rc<T> {
unsafe {
Expand All @@ -97,56 +77,75 @@ impl<T> Clone for Rc<T> {
}

impl<T: DeepClone> DeepClone for Rc<T> {
/// Return a deep copy of the reference counted pointer.
#[inline]
fn deep_clone(&self) -> Rc<T> {
unsafe { Rc::new(self.borrow().deep_clone()) }
unsafe { Rc::new_unchecked(self.borrow().deep_clone()) }
}
}

#[unsafe_destructor]
impl<T> Drop for Rc<T> {
fn drop(&mut self) {
unsafe {
if self.ptr.is_not_null() {
(*self.ptr).count -= 1;
if (*self.ptr).count == 0 {
let _: ~RcBox<T> = transmute(self.ptr);
}
}
}
}
}

#[cfg(test)]
mod test_rc {
use super::*;
use std::cell::Cell;
use cell::Cell;

#[test]
fn test_clone() {
let x = Rc::from_send(Cell::new(5));
let y = x.clone();
do x.borrow().with_mut_ref |inner| {
*inner = 20;
unsafe {
let x = Rc::new_unchecked(Cell::new(5));
let y = x.clone();
do x.borrow().with_mut_ref |inner| {
*inner = 20;
}
assert_eq!(y.borrow().take(), 20);
}
assert_eq!(y.borrow().take(), 20);
}

#[test]
fn test_deep_clone() {
let x = Rc::from_send(Cell::new(5));
let y = x.deep_clone();
do x.borrow().with_mut_ref |inner| {
*inner = 20;
unsafe {
let x = Rc::new_unchecked(Cell::new(5));
let y = x.deep_clone();
do x.borrow().with_mut_ref |inner| {
*inner = 20;
}
assert_eq!(y.borrow().take(), 5);
}
assert_eq!(y.borrow().take(), 5);
}

#[test]
fn test_simple() {
let x = Rc::from_freeze(5);
let x = Rc::new(5);
assert_eq!(*x.borrow(), 5);
}

#[test]
fn test_simple_clone() {
let x = Rc::from_freeze(5);
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::from_send(~5);
assert_eq!(**x.borrow(), 5);
unsafe {
let x = Rc::new_unchecked(~5);
assert_eq!(**x.borrow(), 5);
}
}
}

Expand All @@ -171,21 +170,30 @@ pub struct RcMut<T> {
priv ptr: *mut RcMutBox<T>,
}

impl<T> RcMut<T> {
unsafe fn new(value: T) -> RcMut<T> {
RcMut{ptr: owned_to_raw(~RcMutBox{value: value, count: 1, borrow: Nothing})}
impl<T: Freeze> RcMut<T> {
/// Construct a new mutable reference-counted box from a `Freeze` value
#[inline]
pub fn new(value: T) -> RcMut<T> {
unsafe { RcMut::new_unchecked(value) }
}
}

impl<T: Send> RcMut<T> {
/// Construct a new mutable reference-counted box from a `Send` value
#[inline]
pub fn from_send(value: T) -> RcMut<T> {
unsafe { RcMut::new(value) }
unsafe { RcMut::new_unchecked(value) }
}
}

impl<T: Freeze> RcMut<T> {
pub fn from_freeze(value: T) -> RcMut<T> {
unsafe { RcMut::new(value) }
impl<T> RcMut<T> {
/// Unsafety construct a new mutable reference-counted box from any value.
///
/// It is possible to create cycles, which will leak, and may interact
/// poorly with managed pointers.
#[inline]
pub unsafe fn new_unchecked(value: T) -> RcMut<T> {
RcMut{ptr: transmute(~RcMutBox{value: value, count: 1, borrow: Nothing})}
}
}

Expand Down Expand Up @@ -223,7 +231,7 @@ impl<T> Drop for RcMut<T> {
if self.ptr.is_not_null() {
(*self.ptr).count -= 1;
if (*self.ptr).count == 0 {
let _: ~T = cast::transmute(self.ptr);
let _: ~RcMutBox<T> = transmute(self.ptr);
}
}
}
Expand All @@ -247,7 +255,7 @@ impl<T: DeepClone> DeepClone for RcMut<T> {
fn deep_clone(&self) -> RcMut<T> {
do self.with_borrow |x| {
// FIXME: #6497: should avoid freeze (slow)
unsafe { RcMut::new(x.deep_clone()) }
unsafe { RcMut::new_unchecked(x.deep_clone()) }
}
}
}
Expand All @@ -270,7 +278,7 @@ mod test_rc_mut {

#[test]
fn test_deep_clone() {
let x = RcMut::from_freeze(5);
let x = RcMut::new(5);
let y = x.deep_clone();
do x.with_mut_borrow |value| {
*value = 20;
Expand Down Expand Up @@ -298,7 +306,7 @@ mod test_rc_mut {

#[test]
fn modify() {
let x = RcMut::from_freeze(5);
let x = RcMut::new(5);
let y = x.clone();

do y.with_mut_borrow |a| {
Expand All @@ -320,7 +328,7 @@ mod test_rc_mut {

#[test]
fn release_mutable() {
let x = RcMut::from_freeze(5);
let x = RcMut::new(5);
do x.with_mut_borrow |_| {}
do x.with_borrow |_| {}
}
Expand All @@ -340,7 +348,7 @@ mod test_rc_mut {
#[test]
#[should_fail]
fn mutable_dupe() {
let x = RcMut::from_freeze(5);
let x = RcMut::new(5);
let y = x.clone();

do x.with_mut_borrow |_| {
Expand All @@ -364,7 +372,7 @@ mod test_rc_mut {
#[test]
#[should_fail]
fn restore_freeze() {
let x = RcMut::from_freeze(5);
let x = RcMut::new(5);
let y = x.clone();

do x.with_borrow |_| {
Expand Down
1 change: 1 addition & 0 deletions src/libstd/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ pub mod ptr;
pub mod owned;
pub mod managed;
pub mod borrow;
pub mod rc;


/* Core language traits */
Expand Down
5 changes: 2 additions & 3 deletions src/test/compile-fail/issue-7013.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod extra;
use extra::rc::RcMut;
use std::rc::RcMut;

trait Foo
{
Expand Down Expand Up @@ -37,7 +36,7 @@ struct A
fn main()
{
let a = A {v: ~B{v: None} as ~Foo}; //~ ERROR cannot pack type `~B`, which does not fulfill `Send`
let v = RcMut::from_freeze(a); //~ ERROR instantiating a type parameter with an incompatible type
let v = RcMut::new(a); //~ ERROR instantiating a type parameter with an incompatible type
let w = v.clone();
v.with_mut_borrow(|p| {p.v.set(w.clone());})
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
18 changes: 18 additions & 0 deletions src/test/compile-fail/no_send-rc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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.

use std::rc::Rc;

fn bar<T: Send>(_: T) {}

fn main() {
let x = Rc::new(5);
bar(x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc<int>`, which does not fulfill `Send`
}
File renamed without changes.
8 changes: 4 additions & 4 deletions src/test/compile-fail/rcmut-not-const-and-not-owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod extra;
use std::rc::RcMut;

fn o<T: Send>(_: &T) {}
fn c<T: Freeze>(_: &T) {}

fn main() {
let x = extra::rc::RcMut::from_send(0);
o(&x); //~ ERROR instantiating a type parameter with an incompatible type `extra::rc::RcMut<int>`, which does not fulfill `Send`
c(&x); //~ ERROR instantiating a type parameter with an incompatible type `extra::rc::RcMut<int>`, which does not fulfill `Freeze`
let x = RcMut::from_send(0);
o(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::RcMut<int>`, which does not fulfill `Send`
c(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::RcMut<int>`, which does not fulfill `Freeze`
}

0 comments on commit c147ec6

Please sign in to comment.