Skip to content

Commit bf748e5

Browse files
committed
auto merge of #6241 : thestinger/rust/rc, r=pcwalton
To provide a reference counted pointer type with deterministic destruction once managed boxes are switched over to a garbage collector. Unlike managed boxes, these can be moved instead of just copied/cloned which is helpful for avoiding reference counts. Needs #5601 to be fixed in order for safety to be provided without the current ugly workaround of making the pointers contain `Option<@()>` and `Option<@mut ()>` (which are just set to `None`). @brson: r?
2 parents d2f0235 + 3d526d1 commit bf748e5

File tree

2 files changed

+276
-0
lines changed

2 files changed

+276
-0
lines changed

Diff for: src/libstd/rc.rs

+275
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
/** Task-local reference counted smart pointers
12+
13+
Task-local reference counted smart pointers are an alternative to managed boxes with deterministic
14+
destruction. They are restricted to containing `Owned` types in order to prevent cycles.
15+
16+
*/
17+
18+
use core::libc::{c_void, size_t, malloc, free};
19+
use core::unstable::intrinsics;
20+
21+
struct RcBox<T> {
22+
value: T,
23+
count: uint
24+
}
25+
26+
/// Immutable reference counted pointer type
27+
pub struct Rc<T> {
28+
priv ptr: *mut RcBox<T>,
29+
priv non_owned: Option<@()> // FIXME: #5601: replace with `#[non_owned]`
30+
}
31+
32+
pub impl<'self, T: Owned> Rc<T> {
33+
fn new(value: T) -> Rc<T> {
34+
unsafe {
35+
let ptr = malloc(sys::size_of::<RcBox<T>>() as size_t) as *mut RcBox<T>;
36+
assert!(!ptr::is_null(ptr));
37+
intrinsics::move_val_init(&mut *ptr, RcBox{value: value, count: 1});
38+
Rc{ptr: ptr, non_owned: None}
39+
}
40+
}
41+
42+
#[inline(always)]
43+
fn borrow(&self) -> &'self T {
44+
unsafe { cast::transmute_region(&(*self.ptr).value) }
45+
}
46+
}
47+
48+
#[unsafe_destructor]
49+
impl<T: Owned> Drop for Rc<T> {
50+
fn finalize(&self) {
51+
unsafe {
52+
(*self.ptr).count -= 1;
53+
if (*self.ptr).count == 0 {
54+
let mut x = intrinsics::init();
55+
x <-> *self.ptr;
56+
free(self.ptr as *c_void)
57+
}
58+
}
59+
}
60+
}
61+
62+
impl<T: Owned> Clone for Rc<T> {
63+
#[inline]
64+
fn clone(&self) -> Rc<T> {
65+
unsafe {
66+
(*self.ptr).count += 1;
67+
Rc{ptr: self.ptr, non_owned: None}
68+
}
69+
}
70+
}
71+
72+
#[cfg(test)]
73+
mod test_rc {
74+
use super::*;
75+
76+
#[test]
77+
fn test_simple() {
78+
let x = Rc::new(5);
79+
assert_eq!(*x.borrow(), 5);
80+
}
81+
82+
#[test]
83+
fn test_clone() {
84+
let x = Rc::new(5);
85+
let y = x.clone();
86+
assert_eq!(*x.borrow(), 5);
87+
assert_eq!(*y.borrow(), 5);
88+
}
89+
90+
#[test]
91+
fn test_destructor() {
92+
let x = Rc::new(~5);
93+
assert_eq!(**x.borrow(), 5);
94+
}
95+
}
96+
97+
#[abi = "rust-intrinsic"]
98+
extern "rust-intrinsic" mod rusti {
99+
fn init<T>() -> T;
100+
}
101+
102+
#[deriving(Eq)]
103+
enum Borrow {
104+
Mutable,
105+
Immutable,
106+
Nothing
107+
}
108+
109+
struct RcMutBox<T> {
110+
value: T,
111+
count: uint,
112+
borrow: Borrow
113+
}
114+
115+
/// Mutable reference counted pointer type
116+
pub struct RcMut<T> {
117+
priv ptr: *mut RcMutBox<T>,
118+
priv non_owned: Option<@mut ()> // FIXME: #5601: replace with `#[non_owned]` and `#[non_const]`
119+
}
120+
121+
pub impl<'self, T: Owned> RcMut<T> {
122+
fn new(value: T) -> RcMut<T> {
123+
unsafe {
124+
let ptr = malloc(sys::size_of::<RcMutBox<T>>() as size_t) as *mut RcMutBox<T>;
125+
assert!(!ptr::is_null(ptr));
126+
intrinsics::move_val_init(&mut *ptr, RcMutBox{value: value, count: 1, borrow: Nothing});
127+
RcMut{ptr: ptr, non_owned: None}
128+
}
129+
}
130+
131+
/// Fails if there is already a mutable borrow of the box
132+
#[inline]
133+
fn with_borrow(&self, f: &fn(&T)) {
134+
unsafe {
135+
assert!((*self.ptr).borrow != Mutable);
136+
let previous = (*self.ptr).borrow;
137+
(*self.ptr).borrow = Immutable;
138+
f(cast::transmute_region(&(*self.ptr).value));
139+
(*self.ptr).borrow = previous;
140+
}
141+
}
142+
143+
/// Fails if there is already a mutable or immutable borrow of the box
144+
#[inline]
145+
fn with_mut_borrow(&self, f: &fn(&mut T)) {
146+
unsafe {
147+
assert!((*self.ptr).borrow == Nothing);
148+
(*self.ptr).borrow = Mutable;
149+
f(cast::transmute_mut_region(&mut (*self.ptr).value));
150+
(*self.ptr).borrow = Nothing;
151+
}
152+
}
153+
}
154+
155+
#[unsafe_destructor]
156+
impl<T: Owned> Drop for RcMut<T> {
157+
fn finalize(&self) {
158+
unsafe {
159+
(*self.ptr).count -= 1;
160+
if (*self.ptr).count == 0 {
161+
let mut x = rusti::init();
162+
x <-> *self.ptr;
163+
free(self.ptr as *c_void)
164+
}
165+
}
166+
}
167+
}
168+
169+
impl<T: Owned> Clone for RcMut<T> {
170+
#[inline]
171+
fn clone(&self) -> RcMut<T> {
172+
unsafe {
173+
(*self.ptr).count += 1;
174+
RcMut{ptr: self.ptr, non_owned: None}
175+
}
176+
}
177+
}
178+
179+
#[cfg(test)]
180+
mod test_rc_mut {
181+
use super::*;
182+
183+
#[test]
184+
fn borrow_many() {
185+
let x = RcMut::new(5);
186+
let y = x.clone();
187+
188+
do x.with_borrow |a| {
189+
assert_eq!(*a, 5);
190+
do y.with_borrow |b| {
191+
assert_eq!(*b, 5);
192+
do x.with_borrow |c| {
193+
assert_eq!(*c, 5);
194+
}
195+
}
196+
}
197+
}
198+
199+
#[test]
200+
fn modify() {
201+
let x = RcMut::new(5);
202+
let y = x.clone();
203+
204+
do y.with_mut_borrow |a| {
205+
assert_eq!(*a, 5);
206+
*a = 6;
207+
}
208+
209+
do x.with_borrow |a| {
210+
assert_eq!(*a, 6);
211+
}
212+
}
213+
214+
#[test]
215+
fn release_immutable() {
216+
let x = RcMut::new(5);
217+
do x.with_borrow |_| {}
218+
do x.with_mut_borrow |_| {}
219+
}
220+
221+
#[test]
222+
fn release_mutable() {
223+
let x = RcMut::new(5);
224+
do x.with_mut_borrow |_| {}
225+
do x.with_borrow |_| {}
226+
}
227+
228+
#[test]
229+
#[should_fail]
230+
fn frozen() {
231+
let x = RcMut::new(5);
232+
let y = x.clone();
233+
234+
do x.with_borrow |_| {
235+
do y.with_mut_borrow |_| {
236+
}
237+
}
238+
}
239+
240+
#[test]
241+
#[should_fail]
242+
fn mutable_dupe() {
243+
let x = RcMut::new(5);
244+
let y = x.clone();
245+
246+
do x.with_mut_borrow |_| {
247+
do y.with_mut_borrow |_| {
248+
}
249+
}
250+
}
251+
252+
#[test]
253+
#[should_fail]
254+
fn mutable_freeze() {
255+
let x = RcMut::new(5);
256+
let y = x.clone();
257+
258+
do x.with_mut_borrow |_| {
259+
do y.with_borrow |_| {
260+
}
261+
}
262+
}
263+
264+
#[test]
265+
#[should_fail]
266+
fn restore_freeze() {
267+
let x = RcMut::new(5);
268+
let y = x.clone();
269+
270+
do x.with_borrow |_| {
271+
do x.with_borrow |_| {}
272+
do y.with_mut_borrow |_| {}
273+
}
274+
}
275+
}

Diff for: src/libstd/std.rc

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub mod uv_global_loop;
5050
pub mod c_vec;
5151
pub mod timer;
5252
pub mod io_util;
53+
pub mod rc;
5354

5455
// Concurrency
5556

0 commit comments

Comments
 (0)