-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Add Arc::unwrap_or_drop
for safely discarding Arc
s without calling the destructor on the inner type.
#75911
Add Arc::unwrap_or_drop
for safely discarding Arc
s without calling the destructor on the inner type.
#75911
Changes from 1 commit
a534492
8af2a40
1ceee61
838e5ed
08455a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -500,6 +500,62 @@ impl<T> Arc<T> { | |||||
Ok(elem) | ||||||
} | ||||||
} | ||||||
|
||||||
/// Returns the inner value, if the `Arc` has exactly one strong reference. | ||||||
/// | ||||||
/// Otherwise, [`None`] is returned and the `Arc` is dropped. | ||||||
/// | ||||||
/// This will succeed even if there are outstanding weak references. | ||||||
/// | ||||||
/// If `unwrap_or_drop` is called on every clone of this `Arc`, | ||||||
/// it is guaranteed that exactly one of the calls returns the inner value. | ||||||
/// The similar expression `Arc::try_unwrap(this).ok()` does not | ||||||
/// offer this guarantee. | ||||||
/// | ||||||
/// # Examples | ||||||
/// | ||||||
/// ``` | ||||||
/// #![feature(unwrap_or_drop)] | ||||||
/// | ||||||
/// use std::sync::Arc; | ||||||
/// | ||||||
/// let x = Arc::new(3); | ||||||
/// let y = Arc::clone(&x); | ||||||
/// | ||||||
/// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x)); | ||||||
/// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y)); | ||||||
/// | ||||||
/// let x_unwrapped_value = x_unwrap_thread.join().unwrap(); | ||||||
/// let y_unwrapped_value = y_unwrap_thread.join().unwrap(); | ||||||
/// | ||||||
/// assert!(matches!( | ||||||
/// (x_unwrapped_value, y_unwrapped_value), | ||||||
/// (None, Some(3)) | (Some(3), None) | ||||||
/// )); | ||||||
/// ``` | ||||||
#[inline] | ||||||
#[unstable(feature = "unwrap_or_drop", issue = "none")] // FIXME: add issue | ||||||
// FIXME: should this copy all/some of the comments from drop and drop_slow? | ||||||
pub fn unwrap_or_drop(this: Self) -> Option<T> { | ||||||
// following the implementation of `drop` (and `drop_slow`) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comments in std usually start uppercase. This also applies to other comments that I won't individually comment on.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’ll still have to rework the comments a bit anyways, as suggested above. But thanks for the info anyways. |
||||||
let mut this = core::mem::ManuallyDrop::new(this); | ||||||
|
||||||
if this.inner().strong.fetch_sub(1, Release) != 1 { | ||||||
return None; | ||||||
} | ||||||
|
||||||
acquire!(this.inner().strong); | ||||||
|
||||||
// FIXME: should the part below this be moved into a seperate #[inline(never)] | ||||||
// function, like it's done with drop_slow in drop? | ||||||
Comment on lines
+621
to
+622
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is necessary for now. We can still improve this later, if it seems useful. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was mostly just curious what the reasoning for the separation in the |
||||||
|
||||||
// using `ptr::read` where `drop_slow` was using `ptr::drop_in_place` | ||||||
let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) }; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot: Please write a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The situation is a bit more complex. It is safe because it is doing the same things that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I've done before is say something along the lines of "this is safe for the same reason the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I didn't answer earlier, I didn't get the notification for this comment. The comments in the You can add a line about the differences between |
||||||
|
||||||
drop(Weak { ptr: this.ptr }); | ||||||
|
||||||
Some(inner) | ||||||
} | ||||||
} | ||||||
|
||||||
impl<T> Arc<[T]> { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,45 @@ fn try_unwrap() { | |
assert_eq!(Arc::try_unwrap(x), Ok(5)); | ||
} | ||
|
||
#[test] | ||
fn unwrap_or_drop() { | ||
// FIXME: Is doing this kind of loop reasonable? I tested `Arc::try_unwrap(x).ok()` | ||
// and it makes this kind of assertion fail in roughly every second run somewhere | ||
// between 1000 and 5000 iterations; I feel like doing a single iteration is too | ||
// unlikely to catch anything interesting but doing too many is way too slow | ||
// for a test that wouldn't ever fail for any reasonable implementation | ||
|
||
for _ in 0..100 | ||
// ^ increase chances of hitting uncommon race conditions | ||
{ | ||
use std::sync::Arc; | ||
let x = Arc::new(3); | ||
let y = Arc::clone(&x); | ||
let r_thread = std::thread::spawn(|| Arc::try_unwrap(x).ok()); | ||
let s_thread = std::thread::spawn(|| Arc::try_unwrap(y).ok()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spawning a thread takes a while, so I would almost expect both But yeah, in any case, that's a really tricky test to write. Not sure what's best here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested and the test does indeed fail more often than one would think (on my machine) if |
||
let r = r_thread.join().expect("r_thread panicked"); | ||
let s = s_thread.join().expect("s_thread panicked"); | ||
assert!( | ||
matches!((r, s), (None, Some(3)) | (Some(3), None)), | ||
"assertion failed: unexpected result `{:?}`\ | ||
\n expected `(None, Some(3))` or `(Some(3), None)`", | ||
(r, s), | ||
); | ||
} | ||
|
||
let x = Arc::new(3); | ||
assert_eq!(Arc::unwrap_or_drop(x), Some(3)); | ||
|
||
let x = Arc::new(4); | ||
let y = Arc::clone(&x); | ||
assert_eq!(Arc::unwrap_or_drop(x), None); | ||
assert_eq!(Arc::unwrap_or_drop(y), Some(4)); | ||
|
||
let x = Arc::new(5); | ||
let _w = Arc::downgrade(&x); | ||
assert_eq!(Arc::unwrap_or_drop(x), Some(5)); | ||
} | ||
|
||
#[test] | ||
fn into_from_raw() { | ||
let x = Arc::new(box "hello"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This second long example feels slightly overkill for this kind of highly specialized method. I would assume that people reaching for this method don't need a motivating real world example anymore. However, now that it's here already, you don't need to remove it. Maybe it helps someone after all ^_^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I know, I called it overkill myself. Nontheless, more examples aren’t going to hurt, probably.