Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clean up the Rc type and move it to libstd #9794

Merged
merged 2 commits into from
Oct 11, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -132,6 +132,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());})
}
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`
}
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`
}