Skip to content

Commit a534492

Browse files
committed
Implement Arc::unwrap_or_drop.
Also add documentation and tests for it. This commit has some minor unresolved questions and is intended to be amended.
1 parent ee54128 commit a534492

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

library/alloc/src/sync.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,62 @@ impl<T> Arc<T> {
500500
Ok(elem)
501501
}
502502
}
503+
504+
/// Returns the inner value, if the `Arc` has exactly one strong reference.
505+
///
506+
/// Otherwise, [`None`] is returned and the `Arc` is dropped.
507+
///
508+
/// This will succeed even if there are outstanding weak references.
509+
///
510+
/// If `unwrap_or_drop` is called on every clone of this `Arc`,
511+
/// it is guaranteed that exactly one of the calls returns the inner value.
512+
/// The similar expression `Arc::try_unwrap(this).ok()` does not
513+
/// offer this guarantee.
514+
///
515+
/// # Examples
516+
///
517+
/// ```
518+
/// #![feature(unwrap_or_drop)]
519+
///
520+
/// use std::sync::Arc;
521+
///
522+
/// let x = Arc::new(3);
523+
/// let y = Arc::clone(&x);
524+
///
525+
/// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));
526+
/// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));
527+
///
528+
/// let x_unwrapped_value = x_unwrap_thread.join().unwrap();
529+
/// let y_unwrapped_value = y_unwrap_thread.join().unwrap();
530+
///
531+
/// assert!(matches!(
532+
/// (x_unwrapped_value, y_unwrapped_value),
533+
/// (None, Some(3)) | (Some(3), None)
534+
/// ));
535+
/// ```
536+
#[inline]
537+
#[unstable(feature = "unwrap_or_drop", issue = "none")] // FIXME: add issue
538+
// FIXME: should this copy all/some of the comments from drop and drop_slow?
539+
pub fn unwrap_or_drop(this: Self) -> Option<T> {
540+
// following the implementation of `drop` (and `drop_slow`)
541+
let mut this = core::mem::ManuallyDrop::new(this);
542+
543+
if this.inner().strong.fetch_sub(1, Release) != 1 {
544+
return None;
545+
}
546+
547+
acquire!(this.inner().strong);
548+
549+
// FIXME: should the part below this be moved into a seperate #[inline(never)]
550+
// function, like it's done with drop_slow in drop?
551+
552+
// using `ptr::read` where `drop_slow` was using `ptr::drop_in_place`
553+
let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) };
554+
555+
drop(Weak { ptr: this.ptr });
556+
557+
Some(inner)
558+
}
503559
}
504560

505561
impl<T> Arc<[T]> {

library/alloc/src/sync/tests.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,45 @@ fn try_unwrap() {
101101
assert_eq!(Arc::try_unwrap(x), Ok(5));
102102
}
103103

104+
#[test]
105+
fn unwrap_or_drop() {
106+
// FIXME: Is doing this kind of loop reasonable? I tested `Arc::try_unwrap(x).ok()`
107+
// and it makes this kind of assertion fail in roughly every second run somewhere
108+
// between 1000 and 5000 iterations; I feel like doing a single iteration is too
109+
// unlikely to catch anything interesting but doing too many is way too slow
110+
// for a test that wouldn't ever fail for any reasonable implementation
111+
112+
for _ in 0..100
113+
// ^ increase chances of hitting uncommon race conditions
114+
{
115+
use std::sync::Arc;
116+
let x = Arc::new(3);
117+
let y = Arc::clone(&x);
118+
let r_thread = std::thread::spawn(|| Arc::try_unwrap(x).ok());
119+
let s_thread = std::thread::spawn(|| Arc::try_unwrap(y).ok());
120+
let r = r_thread.join().expect("r_thread panicked");
121+
let s = s_thread.join().expect("s_thread panicked");
122+
assert!(
123+
matches!((r, s), (None, Some(3)) | (Some(3), None)),
124+
"assertion failed: unexpected result `{:?}`\
125+
\n expected `(None, Some(3))` or `(Some(3), None)`",
126+
(r, s),
127+
);
128+
}
129+
130+
let x = Arc::new(3);
131+
assert_eq!(Arc::unwrap_or_drop(x), Some(3));
132+
133+
let x = Arc::new(4);
134+
let y = Arc::clone(&x);
135+
assert_eq!(Arc::unwrap_or_drop(x), None);
136+
assert_eq!(Arc::unwrap_or_drop(y), Some(4));
137+
138+
let x = Arc::new(5);
139+
let _w = Arc::downgrade(&x);
140+
assert_eq!(Arc::unwrap_or_drop(x), Some(5));
141+
}
142+
104143
#[test]
105144
fn into_from_raw() {
106145
let x = Arc::new(box "hello");

0 commit comments

Comments
 (0)