Skip to content

Commit 9b3ec57

Browse files
Rollup merge of rust-lang#51901 - rust-lang:weak-unboxing, r=alexcrichton
Rc: remove unused allocation and fix segfault in Weak::new() Same as rust-lang#50357 did for `Arc`. Fixes rust-lang#48493
2 parents a178cba + 67202b8 commit 9b3ec57

File tree

6 files changed

+192
-44
lines changed

6 files changed

+192
-44
lines changed

src/liballoc/rc.rs

+41-23
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ use core::hash::{Hash, Hasher};
253253
use core::intrinsics::abort;
254254
use core::marker;
255255
use core::marker::{Unsize, PhantomData};
256-
use core::mem::{self, align_of_val, forget, size_of_val, uninitialized};
256+
use core::mem::{self, align_of_val, forget, size_of_val};
257257
use core::ops::Deref;
258258
use core::ops::CoerceUnsized;
259259
use core::ptr::{self, NonNull};
@@ -1153,6 +1153,10 @@ impl<T> From<Vec<T>> for Rc<[T]> {
11531153
/// [`None`]: ../../std/option/enum.Option.html#variant.None
11541154
#[stable(feature = "rc_weak", since = "1.4.0")]
11551155
pub struct Weak<T: ?Sized> {
1156+
// This is a `NonNull` to allow optimizing the size of this type in enums,
1157+
// but it is not necessarily a valid pointer.
1158+
// `Weak::new` sets this to a dangling pointer so that it doesn’t need
1159+
// to allocate space on the heap.
11561160
ptr: NonNull<RcBox<T>>,
11571161
}
11581162

@@ -1165,8 +1169,8 @@ impl<T: ?Sized> !marker::Sync for Weak<T> {}
11651169
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Weak<U>> for Weak<T> {}
11661170

11671171
impl<T> Weak<T> {
1168-
/// Constructs a new `Weak<T>`, allocating memory for `T` without initializing
1169-
/// it. Calling [`upgrade`] on the return value always gives [`None`].
1172+
/// Constructs a new `Weak<T>`, without allocating any memory.
1173+
/// Calling [`upgrade`] on the return value always gives [`None`].
11701174
///
11711175
/// [`upgrade`]: struct.Weak.html#method.upgrade
11721176
/// [`None`]: ../../std/option/enum.Option.html
@@ -1181,18 +1185,18 @@ impl<T> Weak<T> {
11811185
/// ```
11821186
#[stable(feature = "downgraded_weak", since = "1.10.0")]
11831187
pub fn new() -> Weak<T> {
1184-
unsafe {
1185-
Weak {
1186-
ptr: Box::into_raw_non_null(box RcBox {
1187-
strong: Cell::new(0),
1188-
weak: Cell::new(1),
1189-
value: uninitialized(),
1190-
}),
1191-
}
1188+
Weak {
1189+
ptr: NonNull::dangling(),
11921190
}
11931191
}
11941192
}
11951193

1194+
pub(crate) fn is_dangling<T: ?Sized>(ptr: NonNull<T>) -> bool {
1195+
let address = ptr.as_ptr() as *mut () as usize;
1196+
let align = align_of_val(unsafe { ptr.as_ref() });
1197+
address == align
1198+
}
1199+
11961200
impl<T: ?Sized> Weak<T> {
11971201
/// Attempts to upgrade the `Weak` pointer to an [`Rc`], extending
11981202
/// the lifetime of the value if successful.
@@ -1222,13 +1226,25 @@ impl<T: ?Sized> Weak<T> {
12221226
/// ```
12231227
#[stable(feature = "rc_weak", since = "1.4.0")]
12241228
pub fn upgrade(&self) -> Option<Rc<T>> {
1225-
if self.strong() == 0 {
1229+
let inner = self.inner()?;
1230+
if inner.strong() == 0 {
12261231
None
12271232
} else {
1228-
self.inc_strong();
1233+
inner.inc_strong();
12291234
Some(Rc { ptr: self.ptr, phantom: PhantomData })
12301235
}
12311236
}
1237+
1238+
/// Return `None` when the pointer is dangling and there is no allocated `RcBox`,
1239+
/// i.e. this `Weak` was created by `Weak::new`
1240+
#[inline]
1241+
fn inner(&self) -> Option<&RcBox<T>> {
1242+
if is_dangling(self.ptr) {
1243+
None
1244+
} else {
1245+
Some(unsafe { self.ptr.as_ref() })
1246+
}
1247+
}
12321248
}
12331249

12341250
#[stable(feature = "rc_weak", since = "1.4.0")]
@@ -1258,12 +1274,14 @@ impl<T: ?Sized> Drop for Weak<T> {
12581274
/// assert!(other_weak_foo.upgrade().is_none());
12591275
/// ```
12601276
fn drop(&mut self) {
1261-
unsafe {
1262-
self.dec_weak();
1277+
if let Some(inner) = self.inner() {
1278+
inner.dec_weak();
12631279
// the weak count starts at 1, and will only go to zero if all
12641280
// the strong pointers have disappeared.
1265-
if self.weak() == 0 {
1266-
Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
1281+
if inner.weak() == 0 {
1282+
unsafe {
1283+
Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
1284+
}
12671285
}
12681286
}
12691287
}
@@ -1284,7 +1302,9 @@ impl<T: ?Sized> Clone for Weak<T> {
12841302
/// ```
12851303
#[inline]
12861304
fn clone(&self) -> Weak<T> {
1287-
self.inc_weak();
1305+
if let Some(inner) = self.inner() {
1306+
inner.inc_weak()
1307+
}
12881308
Weak { ptr: self.ptr }
12891309
}
12901310
}
@@ -1317,7 +1337,7 @@ impl<T> Default for Weak<T> {
13171337
}
13181338
}
13191339

1320-
// NOTE: We checked_add here to deal with mem::forget safety. In particular
1340+
// NOTE: We checked_add here to deal with mem::forget safely. In particular
13211341
// if you mem::forget Rcs (or Weaks), the ref-count can overflow, and then
13221342
// you can free the allocation while outstanding Rcs (or Weaks) exist.
13231343
// We abort because this is such a degenerate scenario that we don't care about
@@ -1370,12 +1390,10 @@ impl<T: ?Sized> RcBoxPtr<T> for Rc<T> {
13701390
}
13711391
}
13721392

1373-
impl<T: ?Sized> RcBoxPtr<T> for Weak<T> {
1393+
impl<T: ?Sized> RcBoxPtr<T> for RcBox<T> {
13741394
#[inline(always)]
13751395
fn inner(&self) -> &RcBox<T> {
1376-
unsafe {
1377-
self.ptr.as_ref()
1378-
}
1396+
self
13791397
}
13801398
}
13811399

src/liballoc/sync.rs

+24-21
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use core::convert::From;
3434

3535
use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error};
3636
use boxed::Box;
37+
use rc::is_dangling;
3738
use string::String;
3839
use vec::Vec;
3940

@@ -43,9 +44,6 @@ use vec::Vec;
4344
/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
4445
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
4546

46-
/// A sentinel value that is used for the pointer of `Weak::new()`.
47-
const WEAK_EMPTY: usize = 1;
48-
4947
/// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically
5048
/// Reference Counted'.
5149
///
@@ -239,9 +237,9 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
239237
#[stable(feature = "arc_weak", since = "1.4.0")]
240238
pub struct Weak<T: ?Sized> {
241239
// This is a `NonNull` to allow optimizing the size of this type in enums,
242-
// but it is actually not truly "non-null". A `Weak::new()` will set this
243-
// to a sentinel value, instead of needing to allocate some space in the
244-
// heap.
240+
// but it is not necessarily a valid pointer.
241+
// `Weak::new` sets this to a dangling pointer so that it doesn’t need
242+
// to allocate space on the heap.
245243
ptr: NonNull<ArcInner<T>>,
246244
}
247245

@@ -1035,10 +1033,8 @@ impl<T> Weak<T> {
10351033
/// ```
10361034
#[stable(feature = "downgraded_weak", since = "1.10.0")]
10371035
pub fn new() -> Weak<T> {
1038-
unsafe {
1039-
Weak {
1040-
ptr: NonNull::new_unchecked(WEAK_EMPTY as *mut _),
1041-
}
1036+
Weak {
1037+
ptr: NonNull::dangling(),
10421038
}
10431039
}
10441040
}
@@ -1074,11 +1070,7 @@ impl<T: ?Sized> Weak<T> {
10741070
pub fn upgrade(&self) -> Option<Arc<T>> {
10751071
// We use a CAS loop to increment the strong count instead of a
10761072
// fetch_add because once the count hits 0 it must never be above 0.
1077-
let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY {
1078-
return None;
1079-
} else {
1080-
unsafe { self.ptr.as_ref() }
1081-
};
1073+
let inner = self.inner()?;
10821074

10831075
// Relaxed load because any write of 0 that we can observe
10841076
// leaves the field in a permanently zero state (so a
@@ -1109,6 +1101,17 @@ impl<T: ?Sized> Weak<T> {
11091101
}
11101102
}
11111103
}
1104+
1105+
/// Return `None` when the pointer is dangling and there is no allocated `ArcInner`,
1106+
/// i.e. this `Weak` was created by `Weak::new`
1107+
#[inline]
1108+
fn inner(&self) -> Option<&ArcInner<T>> {
1109+
if is_dangling(self.ptr) {
1110+
None
1111+
} else {
1112+
Some(unsafe { self.ptr.as_ref() })
1113+
}
1114+
}
11121115
}
11131116

11141117
#[stable(feature = "arc_weak", since = "1.4.0")]
@@ -1126,10 +1129,10 @@ impl<T: ?Sized> Clone for Weak<T> {
11261129
/// ```
11271130
#[inline]
11281131
fn clone(&self) -> Weak<T> {
1129-
let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY {
1130-
return Weak { ptr: self.ptr };
1132+
let inner = if let Some(inner) = self.inner() {
1133+
inner
11311134
} else {
1132-
unsafe { self.ptr.as_ref() }
1135+
return Weak { ptr: self.ptr };
11331136
};
11341137
// See comments in Arc::clone() for why this is relaxed. This can use a
11351138
// fetch_add (ignoring the lock) because the weak count is only locked
@@ -1204,10 +1207,10 @@ impl<T: ?Sized> Drop for Weak<T> {
12041207
// weak count can only be locked if there was precisely one weak ref,
12051208
// meaning that drop could only subsequently run ON that remaining weak
12061209
// ref, which can only happen after the lock is released.
1207-
let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY {
1208-
return;
1210+
let inner = if let Some(inner) = self.inner() {
1211+
inner
12091212
} else {
1210-
unsafe { self.ptr.as_ref() }
1213+
return
12111214
};
12121215

12131216
if inner.weak.fetch_sub(1, Release) == 1 {

src/liballoc/tests/arc.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2018 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+
use std::any::Any;
12+
use std::sync::{Arc, Weak};
13+
14+
#[test]
15+
fn uninhabited() {
16+
enum Void {}
17+
let mut a = Weak::<Void>::new();
18+
a = a.clone();
19+
assert!(a.upgrade().is_none());
20+
21+
let mut a: Weak<Any> = a; // Unsizing
22+
a = a.clone();
23+
assert!(a.upgrade().is_none());
24+
}
25+
26+
#[test]
27+
fn slice() {
28+
let a: Arc<[u32; 3]> = Arc::new([3, 2, 1]);
29+
let a: Arc<[u32]> = a; // Unsizing
30+
let b: Arc<[u32]> = Arc::from(&[3, 2, 1][..]); // Conversion
31+
assert_eq!(a, b);
32+
33+
// Exercise is_dangling() with a DST
34+
let mut a = Arc::downgrade(&a);
35+
a = a.clone();
36+
assert!(a.upgrade().is_some());
37+
}
38+
39+
#[test]
40+
fn trait_object() {
41+
let a: Arc<u32> = Arc::new(4);
42+
let a: Arc<Any> = a; // Unsizing
43+
44+
// Exercise is_dangling() with a DST
45+
let mut a = Arc::downgrade(&a);
46+
a = a.clone();
47+
assert!(a.upgrade().is_some());
48+
49+
let mut b = Weak::<u32>::new();
50+
b = b.clone();
51+
assert!(b.upgrade().is_none());
52+
let mut b: Weak<Any> = b; // Unsizing
53+
b = b.clone();
54+
assert!(b.upgrade().is_none());
55+
}

src/liballoc/tests/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ extern crate rand;
3232
use std::hash::{Hash, Hasher};
3333
use std::collections::hash_map::DefaultHasher;
3434

35+
mod arc;
3536
mod binary_heap;
3637
mod btree;
3738
mod cow_str;
3839
mod fmt;
3940
mod heap;
4041
mod linked_list;
42+
mod rc;
4143
mod slice;
4244
mod str;
4345
mod string;

src/liballoc/tests/rc.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2018 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+
use std::any::Any;
12+
use std::rc::{Rc, Weak};
13+
14+
#[test]
15+
fn uninhabited() {
16+
enum Void {}
17+
let mut a = Weak::<Void>::new();
18+
a = a.clone();
19+
assert!(a.upgrade().is_none());
20+
21+
let mut a: Weak<Any> = a; // Unsizing
22+
a = a.clone();
23+
assert!(a.upgrade().is_none());
24+
}
25+
26+
#[test]
27+
fn slice() {
28+
let a: Rc<[u32; 3]> = Rc::new([3, 2, 1]);
29+
let a: Rc<[u32]> = a; // Unsizing
30+
let b: Rc<[u32]> = Rc::from(&[3, 2, 1][..]); // Conversion
31+
assert_eq!(a, b);
32+
33+
// Exercise is_dangling() with a DST
34+
let mut a = Rc::downgrade(&a);
35+
a = a.clone();
36+
assert!(a.upgrade().is_some());
37+
}
38+
39+
#[test]
40+
fn trait_object() {
41+
let a: Rc<u32> = Rc::new(4);
42+
let a: Rc<Any> = a; // Unsizing
43+
44+
// Exercise is_dangling() with a DST
45+
let mut a = Rc::downgrade(&a);
46+
a = a.clone();
47+
assert!(a.upgrade().is_some());
48+
49+
let mut b = Weak::<u32>::new();
50+
b = b.clone();
51+
assert!(b.upgrade().is_none());
52+
let mut b: Weak<Any> = b; // Unsizing
53+
b = b.clone();
54+
assert!(b.upgrade().is_none());
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2018 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+
fn main() {
12+
enum Void {}
13+
std::rc::Weak::<Void>::new();
14+
std::sync::Weak::<Void>::new();
15+
}

0 commit comments

Comments
 (0)