Skip to content

Commit cb012a0

Browse files
authored
Rollup merge of rust-lang#60766 - vorner:weak-into-raw, r=sfackler
Weak::into_raw Hello This is my first shot at rust-lang#60728. I'd like to consult it a bit before moving further. The biggest question I have is if this API makes sense. My motivation for it is to be able to store the `Weak` in `AtomicPtr`. For that I don't actually need for the pointer to point to the `T`, any pointer (maybe casted to `usize`) would be good enough, but this mirrors what `Arc` does and could be useful for other things too (like comparing if Arc and Weak point to the same thing without playing with the counts), while some opaque pointer wouldn't. Some secondary questions, if this is deemed desirable are: * The weak pointer may be dangling if it is created by `Weak::new()`. It would make sense to treat this as NULL, but that is incompatible with `T: ?Sized` ‒ both `new()` and `ptr::null()` are available only for sized types. The current implementation is therefore also available only for sized `T`s. It would be possible to allow `?Sized` if the API would be `fn into_raw(self) -> Option<NonNull<T>>` and `fn from_raw(NonNull<T>)`, but that's different API than `Arc` has. What would be preferred? * There's a FIXME in my code about what I suspect could be UB. Is it really UB & how to get the pointer correctly? Is manual offsetting of the pointer the only way? * Am I missing some other necessary thing around the feature gates and such? * Is the documentation understandable? I know writing docs is not my strongest skill :-|. Thinks I'd like to do as part of the PR, but are not yet done: * Turn the referenced issue into tracking issue for the feature flag. * Once the `sync::Weak` is considered reasonable, I'd do the equivalent for `rc::Weak`. * This might call for few more tests than what is currently part of the documentation.
2 parents 23b9b83 + 4f1dcb3 commit cb012a0

File tree

2 files changed

+314
-12
lines changed

2 files changed

+314
-12
lines changed

src/liballoc/rc.rs

Lines changed: 156 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ use core::fmt;
239239
use core::hash::{Hash, Hasher};
240240
use core::intrinsics::abort;
241241
use core::marker::{self, Unpin, Unsize, PhantomData};
242-
use core::mem::{self, align_of_val, forget, size_of_val};
242+
use core::mem::{self, align_of, align_of_val, forget, size_of_val};
243243
use core::ops::{Deref, Receiver, CoerceUnsized, DispatchFromDyn};
244244
use core::pin::Pin;
245245
use core::ptr::{self, NonNull};
@@ -416,11 +416,7 @@ impl<T: ?Sized> Rc<T> {
416416
/// ```
417417
#[stable(feature = "rc_raw", since = "1.17.0")]
418418
pub unsafe fn from_raw(ptr: *const T) -> Self {
419-
// Align the unsized value to the end of the RcBox.
420-
// Because it is ?Sized, it will always be the last field in memory.
421-
let align = align_of_val(&*ptr);
422-
let layout = Layout::new::<RcBox<()>>();
423-
let offset = (layout.size() + layout.padding_needed_for(align)) as isize;
419+
let offset = data_offset(ptr);
424420

425421
// Reverse the offset to find the original RcBox.
426422
let fake_ptr = ptr as *mut RcBox<T>;
@@ -1262,6 +1258,143 @@ impl<T> Weak<T> {
12621258
ptr: NonNull::new(usize::MAX as *mut RcBox<T>).expect("MAX is not 0"),
12631259
}
12641260
}
1261+
1262+
/// Returns a raw pointer to the object `T` pointed to by this `Weak<T>`.
1263+
///
1264+
/// It is up to the caller to ensure that the object is still alive when accessing it through
1265+
/// the pointer.
1266+
///
1267+
/// The pointer may be [`null`] or be dangling in case the object has already been destroyed.
1268+
///
1269+
/// # Examples
1270+
///
1271+
/// ```
1272+
/// #![feature(weak_into_raw)]
1273+
///
1274+
/// use std::rc::{Rc, Weak};
1275+
/// use std::ptr;
1276+
///
1277+
/// let strong = Rc::new(42);
1278+
/// let weak = Rc::downgrade(&strong);
1279+
/// // Both point to the same object
1280+
/// assert!(ptr::eq(&*strong, Weak::as_raw(&weak)));
1281+
/// // The strong here keeps it alive, so we can still access the object.
1282+
/// assert_eq!(42, unsafe { *Weak::as_raw(&weak) });
1283+
///
1284+
/// drop(strong);
1285+
/// // But not any more. We can do Weak::as_raw(&weak), but accessing the pointer would lead to
1286+
/// // undefined behaviour.
1287+
/// // assert_eq!(42, unsafe { *Weak::as_raw(&weak) });
1288+
/// ```
1289+
///
1290+
/// [`null`]: ../../std/ptr/fn.null.html
1291+
#[unstable(feature = "weak_into_raw", issue = "60728")]
1292+
pub fn as_raw(this: &Self) -> *const T {
1293+
match this.inner() {
1294+
None => ptr::null(),
1295+
Some(inner) => {
1296+
let offset = data_offset_sized::<T>();
1297+
let ptr = inner as *const RcBox<T>;
1298+
// Note: while the pointer we create may already point to dropped value, the
1299+
// allocation still lives (it must hold the weak point as long as we are alive).
1300+
// Therefore, the offset is OK to do, it won't get out of the allocation.
1301+
let ptr = unsafe { (ptr as *const u8).offset(offset) };
1302+
ptr as *const T
1303+
}
1304+
}
1305+
}
1306+
1307+
/// Consumes the `Weak<T>` and turns it into a raw pointer.
1308+
///
1309+
/// This converts the weak pointer into a raw pointer, preserving the original weak count. It
1310+
/// can be turned back into the `Weak<T>` with [`from_raw`].
1311+
///
1312+
/// The same restrictions of accessing the target of the pointer as with
1313+
/// [`as_raw`] apply.
1314+
///
1315+
/// # Examples
1316+
///
1317+
/// ```
1318+
/// #![feature(weak_into_raw)]
1319+
///
1320+
/// use std::rc::{Rc, Weak};
1321+
///
1322+
/// let strong = Rc::new(42);
1323+
/// let weak = Rc::downgrade(&strong);
1324+
/// let raw = Weak::into_raw(weak);
1325+
///
1326+
/// assert_eq!(1, Rc::weak_count(&strong));
1327+
/// assert_eq!(42, unsafe { *raw });
1328+
///
1329+
/// drop(unsafe { Weak::from_raw(raw) });
1330+
/// assert_eq!(0, Rc::weak_count(&strong));
1331+
/// ```
1332+
///
1333+
/// [`from_raw`]: struct.Weak.html#method.from_raw
1334+
/// [`as_raw`]: struct.Weak.html#method.as_raw
1335+
#[unstable(feature = "weak_into_raw", issue = "60728")]
1336+
pub fn into_raw(this: Self) -> *const T {
1337+
let result = Self::as_raw(&this);
1338+
mem::forget(this);
1339+
result
1340+
}
1341+
1342+
/// Converts a raw pointer previously created by [`into_raw`] back into `Weak<T>`.
1343+
///
1344+
/// This can be used to safely get a strong reference (by calling [`upgrade`]
1345+
/// later) or to deallocate the weak count by dropping the `Weak<T>`.
1346+
///
1347+
/// It takes ownership of one weak count. In case a [`null`] is passed, a dangling [`Weak`] is
1348+
/// returned.
1349+
///
1350+
/// # Safety
1351+
///
1352+
/// The pointer must represent one valid weak count. In other words, it must point to `T` which
1353+
/// is or *was* managed by an [`Rc`] and the weak count of that [`Rc`] must not have reached
1354+
/// 0. It is allowed for the strong count to be 0.
1355+
///
1356+
/// # Examples
1357+
///
1358+
/// ```
1359+
/// #![feature(weak_into_raw)]
1360+
///
1361+
/// use std::rc::{Rc, Weak};
1362+
///
1363+
/// let strong = Rc::new(42);
1364+
///
1365+
/// let raw_1 = Weak::into_raw(Rc::downgrade(&strong));
1366+
/// let raw_2 = Weak::into_raw(Rc::downgrade(&strong));
1367+
///
1368+
/// assert_eq!(2, Rc::weak_count(&strong));
1369+
///
1370+
/// assert_eq!(42, *Weak::upgrade(&unsafe { Weak::from_raw(raw_1) }).unwrap());
1371+
/// assert_eq!(1, Rc::weak_count(&strong));
1372+
///
1373+
/// drop(strong);
1374+
///
1375+
/// // Decrement the last weak count.
1376+
/// assert!(Weak::upgrade(&unsafe { Weak::from_raw(raw_2) }).is_none());
1377+
/// ```
1378+
///
1379+
/// [`null`]: ../../std/ptr/fn.null.html
1380+
/// [`into_raw`]: struct.Weak.html#method.into_raw
1381+
/// [`upgrade`]: struct.Weak.html#method.upgrade
1382+
/// [`Rc`]: struct.Rc.html
1383+
/// [`Weak`]: struct.Weak.html
1384+
#[unstable(feature = "weak_into_raw", issue = "60728")]
1385+
pub unsafe fn from_raw(ptr: *const T) -> Self {
1386+
if ptr.is_null() {
1387+
Self::new()
1388+
} else {
1389+
// See Rc::from_raw for details
1390+
let offset = data_offset(ptr);
1391+
let fake_ptr = ptr as *mut RcBox<T>;
1392+
let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset));
1393+
Weak {
1394+
ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw"),
1395+
}
1396+
}
1397+
}
12651398
}
12661399

12671400
pub(crate) fn is_dangling<T: ?Sized>(ptr: NonNull<T>) -> bool {
@@ -2007,3 +2140,20 @@ impl<T: ?Sized> AsRef<T> for Rc<T> {
20072140

20082141
#[stable(feature = "pin", since = "1.33.0")]
20092142
impl<T: ?Sized> Unpin for Rc<T> { }
2143+
2144+
unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize {
2145+
// Align the unsized value to the end of the RcBox.
2146+
// Because it is ?Sized, it will always be the last field in memory.
2147+
let align = align_of_val(&*ptr);
2148+
let layout = Layout::new::<RcBox<()>>();
2149+
(layout.size() + layout.padding_needed_for(align)) as isize
2150+
}
2151+
2152+
/// Computes the offset of the data field within ArcInner.
2153+
///
2154+
/// Unlike [`data_offset`], this doesn't need the pointer, but it works only on `T: Sized`.
2155+
fn data_offset_sized<T>() -> isize {
2156+
let align = align_of::<T>();
2157+
let layout = Layout::new::<RcBox<()>>();
2158+
(layout.size() + layout.padding_needed_for(align)) as isize
2159+
}

src/liballoc/sync.rs

Lines changed: 158 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use core::borrow;
1313
use core::fmt;
1414
use core::cmp::{self, Ordering};
1515
use core::intrinsics::abort;
16-
use core::mem::{self, align_of_val, size_of_val};
16+
use core::mem::{self, align_of, align_of_val, size_of_val};
1717
use core::ops::{Deref, Receiver, CoerceUnsized, DispatchFromDyn};
1818
use core::pin::Pin;
1919
use core::ptr::{self, NonNull};
@@ -397,11 +397,7 @@ impl<T: ?Sized> Arc<T> {
397397
/// ```
398398
#[stable(feature = "rc_raw", since = "1.17.0")]
399399
pub unsafe fn from_raw(ptr: *const T) -> Self {
400-
// Align the unsized value to the end of the ArcInner.
401-
// Because it is ?Sized, it will always be the last field in memory.
402-
let align = align_of_val(&*ptr);
403-
let layout = Layout::new::<ArcInner<()>>();
404-
let offset = (layout.size() + layout.padding_needed_for(align)) as isize;
400+
let offset = data_offset(ptr);
405401

406402
// Reverse the offset to find the original ArcInner.
407403
let fake_ptr = ptr as *mut ArcInner<T>;
@@ -1071,6 +1067,144 @@ impl<T> Weak<T> {
10711067
ptr: NonNull::new(usize::MAX as *mut ArcInner<T>).expect("MAX is not 0"),
10721068
}
10731069
}
1070+
1071+
/// Returns a raw pointer to the object `T` pointed to by this `Weak<T>`.
1072+
///
1073+
/// It is up to the caller to ensure that the object is still alive when accessing it through
1074+
/// the pointer.
1075+
///
1076+
/// The pointer may be [`null`] or be dangling in case the object has already been destroyed.
1077+
///
1078+
/// # Examples
1079+
///
1080+
/// ```
1081+
/// #![feature(weak_into_raw)]
1082+
///
1083+
/// use std::sync::{Arc, Weak};
1084+
/// use std::ptr;
1085+
///
1086+
/// let strong = Arc::new(42);
1087+
/// let weak = Arc::downgrade(&strong);
1088+
/// // Both point to the same object
1089+
/// assert!(ptr::eq(&*strong, Weak::as_raw(&weak)));
1090+
/// // The strong here keeps it alive, so we can still access the object.
1091+
/// assert_eq!(42, unsafe { *Weak::as_raw(&weak) });
1092+
///
1093+
/// drop(strong);
1094+
/// // But not any more. We can do Weak::as_raw(&weak), but accessing the pointer would lead to
1095+
/// // undefined behaviour.
1096+
/// // assert_eq!(42, unsafe { *Weak::as_raw(&weak) });
1097+
/// ```
1098+
///
1099+
/// [`null`]: ../../std/ptr/fn.null.html
1100+
#[unstable(feature = "weak_into_raw", issue = "60728")]
1101+
pub fn as_raw(this: &Self) -> *const T {
1102+
match this.inner() {
1103+
None => ptr::null(),
1104+
Some(inner) => {
1105+
let offset = data_offset_sized::<T>();
1106+
let ptr = inner as *const ArcInner<T>;
1107+
// Note: while the pointer we create may already point to dropped value, the
1108+
// allocation still lives (it must hold the weak point as long as we are alive).
1109+
// Therefore, the offset is OK to do, it won't get out of the allocation.
1110+
let ptr = unsafe { (ptr as *const u8).offset(offset) };
1111+
ptr as *const T
1112+
}
1113+
}
1114+
}
1115+
1116+
/// Consumes the `Weak<T>` and turns it into a raw pointer.
1117+
///
1118+
/// This converts the weak pointer into a raw pointer, preserving the original weak count. It
1119+
/// can be turned back into the `Weak<T>` with [`from_raw`].
1120+
///
1121+
/// The same restrictions of accessing the target of the pointer as with
1122+
/// [`as_raw`] apply.
1123+
///
1124+
/// # Examples
1125+
///
1126+
/// ```
1127+
/// #![feature(weak_into_raw)]
1128+
///
1129+
/// use std::sync::{Arc, Weak};
1130+
///
1131+
/// let strong = Arc::new(42);
1132+
/// let weak = Arc::downgrade(&strong);
1133+
/// let raw = Weak::into_raw(weak);
1134+
///
1135+
/// assert_eq!(1, Arc::weak_count(&strong));
1136+
/// assert_eq!(42, unsafe { *raw });
1137+
///
1138+
/// drop(unsafe { Weak::from_raw(raw) });
1139+
/// assert_eq!(0, Arc::weak_count(&strong));
1140+
/// ```
1141+
///
1142+
/// [`from_raw`]: struct.Weak.html#method.from_raw
1143+
/// [`as_raw`]: struct.Weak.html#method.as_raw
1144+
#[unstable(feature = "weak_into_raw", issue = "60728")]
1145+
pub fn into_raw(this: Self) -> *const T {
1146+
let result = Self::as_raw(&this);
1147+
mem::forget(this);
1148+
result
1149+
}
1150+
1151+
/// Converts a raw pointer previously created by [`into_raw`] back into
1152+
/// `Weak<T>`.
1153+
///
1154+
/// This can be used to safely get a strong reference (by calling [`upgrade`]
1155+
/// later) or to deallocate the weak count by dropping the `Weak<T>`.
1156+
///
1157+
/// It takes ownership of one weak count. In case a [`null`] is passed, a dangling [`Weak`] is
1158+
/// returned.
1159+
///
1160+
/// # Safety
1161+
///
1162+
/// The pointer must represent one valid weak count. In other words, it must point to `T` which
1163+
/// is or *was* managed by an [`Arc`] and the weak count of that [`Arc`] must not have reached
1164+
/// 0. It is allowed for the strong count to be 0.
1165+
///
1166+
/// # Examples
1167+
///
1168+
/// ```
1169+
/// #![feature(weak_into_raw)]
1170+
///
1171+
/// use std::sync::{Arc, Weak};
1172+
///
1173+
/// let strong = Arc::new(42);
1174+
///
1175+
/// let raw_1 = Weak::into_raw(Arc::downgrade(&strong));
1176+
/// let raw_2 = Weak::into_raw(Arc::downgrade(&strong));
1177+
///
1178+
/// assert_eq!(2, Arc::weak_count(&strong));
1179+
///
1180+
/// assert_eq!(42, *Weak::upgrade(&unsafe { Weak::from_raw(raw_1) }).unwrap());
1181+
/// assert_eq!(1, Arc::weak_count(&strong));
1182+
///
1183+
/// drop(strong);
1184+
///
1185+
/// // Decrement the last weak count.
1186+
/// assert!(Weak::upgrade(&unsafe { Weak::from_raw(raw_2) }).is_none());
1187+
/// ```
1188+
///
1189+
/// [`null`]: ../../std/ptr/fn.null.html
1190+
/// [`into_raw`]: struct.Weak.html#method.into_raw
1191+
/// [`upgrade`]: struct.Weak.html#method.upgrade
1192+
/// [`Weak`]: struct.Weak.html
1193+
/// [`Arc`]: struct.Arc.html
1194+
#[unstable(feature = "weak_into_raw", issue = "60728")]
1195+
pub unsafe fn from_raw(ptr: *const T) -> Self {
1196+
if ptr.is_null() {
1197+
Self::new()
1198+
} else {
1199+
// See Arc::from_raw for details
1200+
let offset = data_offset(ptr);
1201+
let fake_ptr = ptr as *mut ArcInner<T>;
1202+
let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset));
1203+
Weak {
1204+
ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw"),
1205+
}
1206+
}
1207+
}
10741208
}
10751209

10761210
impl<T: ?Sized> Weak<T> {
@@ -2150,3 +2284,21 @@ impl<T: ?Sized> AsRef<T> for Arc<T> {
21502284

21512285
#[stable(feature = "pin", since = "1.33.0")]
21522286
impl<T: ?Sized> Unpin for Arc<T> { }
2287+
2288+
/// Computes the offset of the data field within ArcInner.
2289+
unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize {
2290+
// Align the unsized value to the end of the ArcInner.
2291+
// Because it is ?Sized, it will always be the last field in memory.
2292+
let align = align_of_val(&*ptr);
2293+
let layout = Layout::new::<ArcInner<()>>();
2294+
(layout.size() + layout.padding_needed_for(align)) as isize
2295+
}
2296+
2297+
/// Computes the offset of the data field within ArcInner.
2298+
///
2299+
/// Unlike [`data_offset`], this doesn't need the pointer, but it works only on `T: Sized`.
2300+
fn data_offset_sized<T>() -> isize {
2301+
let align = align_of::<T>();
2302+
let layout = Layout::new::<ArcInner<()>>();
2303+
(layout.size() + layout.padding_needed_for(align)) as isize
2304+
}

0 commit comments

Comments
 (0)