Summary
This is an RFC to add APIs to std::rc::Weak
to allow recover shared object after every Rc
s are dropped.
Identical APIs will be added for std::sync::Weak
.
Also, it proposes additional guarantee to align of Rc
's and Arc
's underlying pointer that it should be greater than or equal to align of usize
.
Motivation
Currently rc::Weak
doesn’t own shared object, but still own memory allocation itself. It means shared object will be drop()
ed when every Rc
s are dropped, but underlying memory will not be free
d until every Weak
s also are dropped.
But after shared object is dropped, Weak
becomes completely useless even it still owns shared allocation. It’s conceptually like shared Box<Option<T>>
, but cannot turn it “on” after turned off.
Guide-level explanation
This RFC proposes some methods to allow re-construct shared object from std::rc::Weak
handle using existing allocated memory after shared object is dropped. It also covers std::sync::Weak
, with non-blocking atomic operations.
Also, with this RFC Weak::new()
becomes useful as it can be used for lazy-construction of shared object. So this RFC also propose small change to semantics of Weak::new()
that it does not allocate memory until recovered or cloned. To do this without change Weak
's size and make it zero-cost, additional guarantee will be added that align of RcBox
is equal or greater than of usize
.
Reference-level explanation
Pointer alignment
Result of Rc::into_raw(Self) -> *const T
and Arc::into_raw(Self) -> *const T
pointer must be aligned with at least mem::align_of::<usize>()
.
Additional methods of std::rc::Weak<T>
/// Returns `true` if underlying object is alive
fn can_upgrade(&self) -> bool;
/// Try upgrade, and recover if failed
fn upgrade_or_recover<F: FnOnce() -> T>(&self, f: F) -> Rc<T>;
Additional methods of std::sync::Weak<T>
/// Returns `true` if underlying object is alive
/// Note that this state can always be changed by other threads
fn can_upgrade(&self) -> bool;
/// Try upgrade, and try recover if failed
/// Return `Err` if another thread is already doing it.
fn try_upgrade_or_recover<F: FnOnce() -> T>(&self, f: F) -> Result<Arc<T>, RecoverError>;
Changed semantics
Both std::rc::Weak::new()
and std::sync::Weak::new()
will not allocate memory until cloned or recovered.
Drawbacks
This adds complexities to stdlib.
Alternatives
-
Simulate this as a crate using
Rc<RefCell<Option<T>>>
. -
Skip changing semantics of
Weak::new()
. -
Skip guaranteed pointer alignment.
-
Only implement this for
Rc
and skip forArc
Unresolved questions
Should we need another recovery method that does not tries to upgrade first?