Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking issue for Rc/Arc stabilization #27718

Closed
aturon opened this issue Aug 12, 2015 · 29 comments · Fixed by #28339
Closed

Tracking issue for Rc/Arc stabilization #27718

aturon opened this issue Aug 12, 2015 · 29 comments · Fixed by #28339
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Milestone

Comments

@aturon
Copy link
Member

aturon commented Aug 12, 2015

There are a number of unstable features related to Rc and Arc, including both weak references and uniqueness. An RFC to remove weak pointers failed, and the current consensus seems to be to stabilize these APIs after nailing down more precise semantics for uniqueness.

cc @gankro

@aturon aturon added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. B-unstable Blocker: Implemented in the nightly compiler and unstable. labels Aug 12, 2015
@abonander
Copy link
Contributor

Please consider #24789. Dunno if it belongs here or in the RFCs repo since it's a semantics change that wouldn't break much.

@Gankra
Copy link
Contributor

Gankra commented Aug 12, 2015

Current plan:

  • Stabilize weak pointers (Do we want it as a normal method? General convention is static methods -- but it's symmetric to clone)
  • Stabilize try_unwrap only looking at strong count (but properly considering weak count to clean up)
  • Stabilize make_unique which clones if strong != 1 || weak != 0
  • Stabilize get_mut which only succeeds if strong == 1 && weak == 0
  • don't stabilize is_unique because there are two notions of uniqueness (as seen above) -- can consider exposing both notions, but they're both racey to act on in Arc, I think?
  • maybe stabilize strong_count and weak_count? They're harmless to support. However they're inherently racey to act on in Arc, which makes them a bit questionable.

Basically: stabilize the actually useful functionality, don't stabilize introspection pending more work.

@Gankra
Copy link
Contributor

Gankra commented Aug 12, 2015

CC @SimonSapin

@Gankra
Copy link
Contributor

Gankra commented Aug 12, 2015

Oh and maybe bikeshed naming? They seem perfectly cromulant to me, though make_unique has the annoying notion of uniqueness. make_mut is a potential alternative, hinting at the CoW semantics.

@Gankra
Copy link
Contributor

Gankra commented Aug 12, 2015

I am currently drafting a PR to this affect.

@jethrogb
Copy link
Contributor

Is alloc::arc::strong_count part of this issue or #27700?

However they're inherently racey to act on in Arc, which makes them a bit questionable.

It's not racy if you protect the Arc with a Mutex. I have a use case for this in namedlock-rs.

@Gankra
Copy link
Contributor

Gankra commented Aug 12, 2015

@jethrogb this issue -- Arc is defined in liballoc but it's intended to be used inside std::sync

That issue largely concerns the actual alloc::heap API

@jethrogb
Copy link
Contributor

Thanks for the clarifcation. I mention this since using it currently requires the alloc feature. Nevermind, there is the arc_counts feature as of 1.2.0.

@Gankra
Copy link
Contributor

Gankra commented Aug 12, 2015

@jethrogb it only needs that feature because you're accessing the functionality through the alloc crate instead of std. The whole alloc crate is unstable to directly access.

@SimonSapin
Copy link
Contributor

Of things that is still unstable but not deprecated in #27760:

  • I think arc_counts methods should be deprecated since they are racy.

  • Rc::is_unique with its current meaning (strong == 1 && weak == 0) can be emulated with Rc::get_mut(x).is_some(), so I don’t mind deprecating it.

  • Rc::try_unwrap(x).is_ok(), however, would be destructive. I have a use case for testing Rc::strong_count(x) == 1: I want to know whether Node will be dropped when a given Rc<Node> is dropped so I can start deconstructing it to make Drop non-recursive. I could use Rc::try_unwrap, but keeping (a stack of) Rc<Node> around is much cheaper than moving Node with Rc::try_unwrap and keeping (a stack of) Node around, since Node is much larger than Rc<Node> (which is pointer-sized).

    Therefore, I don’t mind deprecating Rc::strong_count and Rc::weak_count as long as there is some way to test Rc::strong_count(x) == 1 non-destructively. Perhaps a dedicated method? But I don’t have a good name in mind, since is_unique is somewhat ambiguous.

@Gankra
Copy link
Contributor

Gankra commented Aug 14, 2015

@SimonSapin sounds like all you need is would_unwrap?

@alexcrichton
Copy link
Member

This feature is now entering its final comment period for stabilization.

I believe that @gankro will post a PR soon with various tweaks to do the final prep for stabilization

@Gankra
Copy link
Contributor

Gankra commented Aug 14, 2015

Building now and dealing with fallout. Changes proposed:

  • rc.make_unique -> Rc::make_mut(&mut rc)
  • add Arc::make_mut
  • deprecate strong_count and weak_count
  • deprecate is_unique
  • add Rc::would_unwrap(&Rc) to evaluate whether try_unwrap will succeed
  • don't add Arc::would_unwrap because it's racy
  • rc.downgrade() -> Rc::downgrade(&Rc) -- note that there cannot be a deprecation grace period due to UFCS naming conflicts!
  • arc.downgrade() -> Arc::downgrade(&Arc) -- ditto!

@alexcrichton
Copy link
Member

@gankro that sounds good to me, but I'd consider Rc::would_unwrap separately for stabilization from the rest of these features.

@jethrogb
Copy link
Contributor

  • rc.make_unique -> Rc::make_mut(&mut rc)
  • add Arc::make_mut

What's happening with Arc::make_unique?

  • don't add Arc::would_unwrap because it's racy

What about Arc::try_unwrap?

  • deprecate strong_count and weak_count

I don't think strong_count/weak_count should be deprecated. Consider a data structure that opaquely owns some data (say, keys in a HashMap). You store an Rc/Arc in it. Now, if you also have a copy of that Rc/Arc, there is no way (after removing the count accessors) to tell whether you have the last copy besides the one in the data structure. Since they are racy for Arc, I propose declaring those unsafe instead, signaling to users that they need to manually synchronize the use.

@Gankra
Copy link
Contributor

Gankra commented Aug 14, 2015

@jethrogb Oh whoops, I got make_unique and try_unwrap a bit mixed up.

I'm not a fan of that argument -- if you want to intropsect uniqueness you can use try_unwrap/would_unwrap and get_mut.

@jethrogb
Copy link
Contributor

if you want to intropsect uniqueness you can use try_unwrap/would_unwrap and get_mut.

Except I just told you that those functions are not sufficient.

@Gankra
Copy link
Contributor

Gankra commented Aug 14, 2015

@jethrogb Ah yes, I misread the example.

Still, this seems extraordinarily niche.

Also: that's not what unsafe is for. It's impossible to introduce memory safety with the counts -- the information they yield just borders on useless (you need to do proper CAS-loop stuff, which only the guts of Arc is prepared to do).

@jethrogb
Copy link
Contributor

Code example

#![feature(arc_unique,arc_counts)]

use std::sync::Arc;
use std::hash::{Hash,Hasher};
use std::collections::HashMap;

#[derive(Clone)]
pub struct ArcHashKey<T>(pub Arc<T>);
impl<T: PartialEq> PartialEq for ArcHashKey<T> {
    fn eq(&self, other: &ArcHashKey<T>) -> bool { self.0.eq(&other.0) }
}
impl<T: Eq> Eq for ArcHashKey<T> { }
impl<T: Hash> Hash for ArcHashKey<T> {
    fn hash<H: Hasher>(&self, state: &mut H) { self.0.hash(state) }
}

fn main() {
    let mut map=HashMap::new();
    let mut key=ArcHashKey(Arc::new("key"));
    map.insert(key.clone(),"value");
    // hand out some references to key, thread some things
    if Arc::strong_count(&key.0)==2 {
        map.remove(&key);
        println!("all threads are done with the arc: {:?}",Arc::get_mut(&mut key.0));
    }
}

@SimonSapin
Copy link
Contributor

@gankro Yes, Rc::would_unwrap(&Rc<T>) -> bool works for my use case described above.

@eefriedman
Copy link
Contributor

One method that hasn't been proposed is a variant of get_mut() which only considers the number of strong references (and moves the contents to a new allocation if necessary); would that be useful?

@Gankra
Copy link
Contributor

Gankra commented Aug 14, 2015

@eefriedman you need to consider the weak references in general because otherwise they can promote themselves to a strong reference and then access the supposedly unaliased &mut. Or am I misunderstanding?

@SimonSapin
Copy link
Contributor

I think @eefriedman means something equivalent to if Rc::would_unwrap(x) { Some(Rc::make_mut(x)) } else { None }.

Gankra added a commit to Gankra/rust that referenced this issue Aug 19, 2015
* Add `Rc::would_unwrap(&Self) -> bool` to introspect whether try_unwrap would succeed,
  because it's destructive (unlike get_mut).
* Move `rc.downgrade()` to `Rc::downgrade(&Self)` per conventions.
* Deprecate `Rc::weak_count` and `Rc::strong_count` for questionable utility.
* Deprecate `Rc::is_unique` for questionable semantics (there are two kinds of
  uniqueness with Weak pointers in play).
* Rename `rc.make_unique()` to `Rc::make_mut(&mut Self)` per conventions, to
  avoid uniqueness terminology, and to clarify the relation to `Rc::get_mut`.
Gankra added a commit to Gankra/rust that referenced this issue Aug 19, 2015
* Add previously omitted function `Arc::try_unwrap(Self) -> Result<T, Self>`
* Move `arc.downgrade()` to `Arc::downgrade(&Self)` per conventions.
* Deprecate `Arc::weak_count` and `Arc::strong_count` for raciness. It is almost
  impossible to correctly act on these results without a CAS loop on the actual
  fields.
* Rename `Arc::make_unique` to `Arc::make_mut` to avoid uniqueness terminology
  and to clarify relation to `Arc::get_mut`.
bors added a commit that referenced this issue Aug 21, 2015
This prepares both for the FCP of #27718

Arc:

* Add previously omitted function `Arc::try_unwrap(Self) -> Result<T, Self>`
* Move `arc.downgrade()` to `Arc::downgrade(&Self)` per conventions.
* Deprecate `Arc::weak_count` and `Arc::strong_count` for raciness. It is almost
  impossible to correctly act on these results without a CAS loop on the actual
  fields.
* Rename `Arc::make_unique` to `Arc::make_mut` to avoid uniqueness terminology 
  and to clarify relation to `Arc::get_mut`.
 

Rc:
* Add `Rc::would_unwrap(&Self) -> bool` to introspect whether try_unwrap would succeed, 
  because it's destructive (unlike get_mut).
* Move `rc.downgrade()` to `Rc::downgrade(&Self)` per conventions.
* Deprecate `Rc::weak_count` and `Rc::strong_count` for questionable utility.
* Deprecate `Rc::is_unique` for questionable semantics (there are two kinds of 
  uniqueness with Weak pointers in play).
* Rename `rc.make_unique()` to `Rc::make_mut(&mut Self)` per conventions, to
  avoid uniqueness terminology, and to clarify the relation to `Rc::get_mut`.

Notable omission:

* Arc::would_unwrap is not added due to the fact that it's racy, and therefore doesn't 
  provide much actionable information.

(note: rc_would_unwrap is not proposed for FCP as it is truly experimental)


r? @aturon (careful attention needs to be taken for the new Arc::try_unwrap, but intuitively it should "just work" by virtue of being semantically equivalent to Drop).
@SimonSapin
Copy link
Contributor

With #27837 landed this is looking pretty good to me. What’s the next step in the process? I think we can stabilize the rc_weak, rc_unique, arc_weak, and arc_unique features as-is. I’d like to stabilize rc_would_unwrap as well, but I’m the only one who asked for it :) This would only leave rc_counts and arc_counts unstable.

@Gankra
Copy link
Contributor

Gankra commented Aug 22, 2015

Stabilize *_weak and *_unique is my proposal as well.

rc_would_unwrap definitely needs more time to bake as unstable

I'd like to deprecated *_counts, though there is some opposition to this (even arc_counts can arguably be used correctly if you're very careful).

@alexcrichton
Copy link
Member

Nominating for discussion to move into FCP

@alexcrichton
Copy link
Member

The libs team decided today to move this into FCP, specifically:

  • std::rc::Weak
  • std::rc::Weak::upgrade
  • std::rc::Rc::downgrade
  • std::rc::Rc::try_unwrap
  • std::rc::Rc::get_mut
  • std::rc::Rc::make_mut
  • std::sync::Weak
  • std::sync::Weak::upgrade
  • std::sync::Arc::downgrade
  • std::sync::Arc::try_unwrap
  • std::sync::Arc::make_mut
  • std::sync::Arc::get_mut

@alexcrichton alexcrichton added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed I-nominated labels Aug 27, 2015
@jethrogb
Copy link
Contributor

I'm not quite sure about the process here. You don't mention the count methods. Does that mean that they'll be deprecated, or that no decision is being made just yet?

@alexcrichton
Copy link
Member

Ah yes, sorry, all other methods will remain unstable for now, and follow-up issues will be opened to track their unstable progress.

alexcrichton added a commit to alexcrichton/rust that referenced this issue Sep 13, 2015
The FCP is coming to a close and 1.4 is coming out soon, so this brings in the
libs team decision for all library features this cycle.

Stabilized APIs:

* `<Box<str>>::into_string`
* `Arc::downgrade`
* `Arc::get_mut`
* `Arc::make_mut`
* `Arc::try_unwrap`
* `Box::from_raw`
* `Box::into_raw`
* `CStr::to_str`
* `CStr::to_string_lossy`
* `CString::from_raw`
* `CString::into_raw`
* `IntoRawFd::into_raw_fd`
* `IntoRawFd`
* `IntoRawHandle::into_raw_handle`
* `IntoRawHandle`
* `IntoRawSocket::into_raw_socket`
* `IntoRawSocket`
* `Rc::downgrade`
* `Rc::get_mut`
* `Rc::make_mut`
* `Rc::try_unwrap`
* `Result::expect`
* `String::into_boxed_slice`
* `TcpSocket::read_timeout`
* `TcpSocket::set_read_timeout`
* `TcpSocket::set_write_timeout`
* `TcpSocket::write_timeout`
* `UdpSocket::read_timeout`
* `UdpSocket::set_read_timeout`
* `UdpSocket::set_write_timeout`
* `UdpSocket::write_timeout`
* `Vec::append`
* `Vec::split_off`
* `VecDeque::append`
* `VecDeque::retain`
* `VecDeque::split_off`
* `rc::Weak::upgrade`
* `rc::Weak`
* `slice::Iter::as_slice`
* `slice::IterMut::into_slice`
* `str::CharIndices::as_str`
* `str::Chars::as_str`
* `str::split_at_mut`
* `str::split_at`
* `sync::Weak::upgrade`
* `sync::Weak`
* `thread::park_timeout`
* `thread::sleep`

Deprecated APIs

* `BTreeMap::with_b`
* `BTreeSet::with_b`
* `Option::as_mut_slice`
* `Option::as_slice`
* `Result::as_mut_slice`
* `Result::as_slice`
* `f32::from_str_radix`
* `f64::from_str_radix`

Closes rust-lang#27277
Closes rust-lang#27718
Closes rust-lang#27736
Closes rust-lang#27764
Closes rust-lang#27765
Closes rust-lang#27766
Closes rust-lang#27767
Closes rust-lang#27768
Closes rust-lang#27769
Closes rust-lang#27771
Closes rust-lang#27773
Closes rust-lang#27775
Closes rust-lang#27776
Closes rust-lang#27785
Closes rust-lang#27792
Closes rust-lang#27795
Closes rust-lang#27797
bors added a commit that referenced this issue Sep 13, 2015
The FCP is coming to a close and 1.4 is coming out soon, so this brings in the
libs team decision for all library features this cycle.

Stabilized APIs:

* `<Box<str>>::into_string`
* `Arc::downgrade`
* `Arc::get_mut`
* `Arc::make_mut`
* `Arc::try_unwrap`
* `Box::from_raw`
* `Box::into_raw`
* `CStr::to_str`
* `CStr::to_string_lossy`
* `CString::from_raw`
* `CString::into_raw`
* `IntoRawFd::into_raw_fd`
* `IntoRawFd`
* `IntoRawHandle::into_raw_handle`
* `IntoRawHandle`
* `IntoRawSocket::into_raw_socket`
* `IntoRawSocket`
* `Rc::downgrade`
* `Rc::get_mut`
* `Rc::make_mut`
* `Rc::try_unwrap`
* `Result::expect`
* `String::into_boxed_slice`
* `TcpSocket::read_timeout`
* `TcpSocket::set_read_timeout`
* `TcpSocket::set_write_timeout`
* `TcpSocket::write_timeout`
* `UdpSocket::read_timeout`
* `UdpSocket::set_read_timeout`
* `UdpSocket::set_write_timeout`
* `UdpSocket::write_timeout`
* `Vec::append`
* `Vec::split_off`
* `VecDeque::append`
* `VecDeque::retain`
* `VecDeque::split_off`
* `rc::Weak::upgrade`
* `rc::Weak`
* `slice::Iter::as_slice`
* `slice::IterMut::into_slice`
* `str::CharIndices::as_str`
* `str::Chars::as_str`
* `str::split_at_mut`
* `str::split_at`
* `sync::Weak::upgrade`
* `sync::Weak`
* `thread::park_timeout`
* `thread::sleep`

Deprecated APIs

* `BTreeMap::with_b`
* `BTreeSet::with_b`
* `Option::as_mut_slice`
* `Option::as_slice`
* `Result::as_mut_slice`
* `Result::as_slice`
* `f32::from_str_radix`
* `f64::from_str_radix`

Closes #27277
Closes #27718
Closes #27736
Closes #27764
Closes #27765
Closes #27766
Closes #27767
Closes #27768
Closes #27769
Closes #27771
Closes #27773
Closes #27775
Closes #27776
Closes #27785
Closes #27792
Closes #27795
Closes #27797
@steveklabnik steveklabnik added this to the 1.3 milestone Sep 18, 2015
@steveklabnik steveklabnik modified the milestones: 1.4, 1.3 Oct 1, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants