Skip to content

Commit ef3d7ee

Browse files
committed
Use an aligned dangling pointer in Weak::new, rather than address 1
1 parent 2c1a715 commit ef3d7ee

File tree

1 file changed

+39
-21
lines changed

1 file changed

+39
-21
lines changed

src/liballoc/sync.rs

+39-21
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ use vec::Vec;
4343
/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
4444
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
4545

46-
/// A sentinel value that is used for the pointer of `Weak::new()`.
47-
const WEAK_EMPTY: usize = 1;
48-
4946
/// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically
5047
/// Reference Counted'.
5148
///
@@ -239,9 +236,9 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
239236
#[stable(feature = "arc_weak", since = "1.4.0")]
240237
pub struct Weak<T: ?Sized> {
241238
// 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.
239+
// but it is not necessarily a valid pointer.
240+
// `Weak::new` sets this to a dangling pointer so that it doesn’t need
241+
// to allocate space on the heap.
245242
ptr: NonNull<ArcInner<T>>,
246243
}
247244

@@ -1034,14 +1031,28 @@ impl<T> Weak<T> {
10341031
/// ```
10351032
#[stable(feature = "downgraded_weak", since = "1.10.0")]
10361033
pub fn new() -> Weak<T> {
1037-
unsafe {
1038-
Weak {
1039-
ptr: NonNull::new_unchecked(WEAK_EMPTY as *mut _),
1040-
}
1034+
Weak {
1035+
ptr: <ArcInner<T>>::dangling_ptr(),
10411036
}
10421037
}
10431038
}
10441039

1040+
// Specialization because `NonNull::<T>::dangling()` only exits for `T: Sized`
1041+
trait Dangling {
1042+
fn dangling_ptr() -> NonNull<Self>;
1043+
fn is_dangling(ptr: NonNull<Self>) -> bool;
1044+
}
1045+
1046+
impl<T: ?Sized> Dangling for T {
1047+
#[inline] default fn dangling_ptr() -> NonNull<Self> { unimplemented!() }
1048+
#[inline] default fn is_dangling(_ptr: NonNull<Self>) -> bool { false }
1049+
}
1050+
1051+
impl<T: Sized> Dangling for T {
1052+
#[inline] fn dangling_ptr() -> NonNull<Self> { NonNull::dangling() }
1053+
#[inline] fn is_dangling(ptr: NonNull<Self>) -> bool { ptr == NonNull::dangling() }
1054+
}
1055+
10451056
impl<T: ?Sized> Weak<T> {
10461057
/// Attempts to upgrade the `Weak` pointer to an [`Arc`], extending
10471058
/// the lifetime of the value if successful.
@@ -1073,11 +1084,7 @@ impl<T: ?Sized> Weak<T> {
10731084
pub fn upgrade(&self) -> Option<Arc<T>> {
10741085
// We use a CAS loop to increment the strong count instead of a
10751086
// fetch_add because once the count hits 0 it must never be above 0.
1076-
let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY {
1077-
return None;
1078-
} else {
1079-
unsafe { self.ptr.as_ref() }
1080-
};
1087+
let inner = self.inner()?;
10811088

10821089
// Relaxed load because any write of 0 that we can observe
10831090
// leaves the field in a permanently zero state (so a
@@ -1108,6 +1115,17 @@ impl<T: ?Sized> Weak<T> {
11081115
}
11091116
}
11101117
}
1118+
1119+
/// Return `None` when the pointer is dangling and there is no allocated `ArcInner`,
1120+
/// i.e. this `Weak` was created by `Weak::new`
1121+
#[inline]
1122+
fn inner(&self) -> Option<&ArcInner<T>> {
1123+
if <ArcInner<T>>::is_dangling(self.ptr) {
1124+
None
1125+
} else {
1126+
Some(unsafe { self.ptr.as_ref() })
1127+
}
1128+
}
11111129
}
11121130

11131131
#[stable(feature = "arc_weak", since = "1.4.0")]
@@ -1125,10 +1143,10 @@ impl<T: ?Sized> Clone for Weak<T> {
11251143
/// ```
11261144
#[inline]
11271145
fn clone(&self) -> Weak<T> {
1128-
let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY {
1129-
return Weak { ptr: self.ptr };
1146+
let inner = if let Some(inner) = self.inner() {
1147+
inner
11301148
} else {
1131-
unsafe { self.ptr.as_ref() }
1149+
return Weak { ptr: self.ptr };
11321150
};
11331151
// See comments in Arc::clone() for why this is relaxed. This can use a
11341152
// fetch_add (ignoring the lock) because the weak count is only locked
@@ -1203,10 +1221,10 @@ impl<T: ?Sized> Drop for Weak<T> {
12031221
// weak count can only be locked if there was precisely one weak ref,
12041222
// meaning that drop could only subsequently run ON that remaining weak
12051223
// ref, which can only happen after the lock is released.
1206-
let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY {
1207-
return;
1224+
let inner = if let Some(inner) = self.inner() {
1225+
inner
12081226
} else {
1209-
unsafe { self.ptr.as_ref() }
1227+
return
12101228
};
12111229

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

0 commit comments

Comments
 (0)