@@ -469,6 +469,17 @@ impl<T> Arc<T> {
469
469
///
470
470
/// This will succeed even if there are outstanding weak references.
471
471
///
472
+ /// It is strongly recommended to use [`Arc::unwrap_or_drop`] instead if you don't
473
+ /// want to keep the `Arc` in the [`Err`] case.
474
+ /// Immediately dropping the [`Err`] payload, like in the expression
475
+ /// `Arc::try_unwrap(this).ok()`, can still cause the strong count to
476
+ /// drop to zero and the inner value of the `Arc` to be dropped:
477
+ /// For instance if two threads execute this expression in parallel, then
478
+ /// there is a race condition. The threads could first both check whether they
479
+ /// have the last clone of their `Arc` via `try_unwrap`, and then
480
+ /// both drop their `Arc` in the call to [`ok`][`Result::ok`],
481
+ /// taking the strong count from two down to zero.
482
+ ///
472
483
/// # Examples
473
484
///
474
485
/// ```
@@ -509,8 +520,11 @@ impl<T> Arc<T> {
509
520
///
510
521
/// If `unwrap_or_drop` is called on every clone of this `Arc`,
511
522
/// it is guaranteed that exactly one of the calls returns the inner value.
523
+ /// This means in particular that the inner value is not dropped.
524
+ ///
512
525
/// The similar expression `Arc::try_unwrap(this).ok()` does not
513
- /// offer this guarantee.
526
+ /// offer such a guarantee. See the last example below and the documentation
527
+ /// of `try_unwrap`[`Arc::try_unwrap`].
514
528
///
515
529
/// # Examples
516
530
///
@@ -522,16 +536,67 @@ impl<T> Arc<T> {
522
536
/// let x = Arc::new(3);
523
537
/// let y = Arc::clone(&x);
524
538
///
539
+ /// // Two threads calling `unwrap_or_drop` on both clones of an `Arc`:
525
540
/// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));
526
541
/// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));
527
542
///
528
543
/// let x_unwrapped_value = x_unwrap_thread.join().unwrap();
529
544
/// let y_unwrapped_value = y_unwrap_thread.join().unwrap();
530
545
///
546
+ /// // One of the threads is guaranteed to receive the inner value:
531
547
/// assert!(matches!(
532
548
/// (x_unwrapped_value, y_unwrapped_value),
533
549
/// (None, Some(3)) | (Some(3), None)
534
550
/// ));
551
+ ///
552
+ ///
553
+ ///
554
+ /// // For a somewhat more practical example,
555
+ /// // we define a singly linked list using `Arc`:
556
+ /// #[derive(Clone)]
557
+ /// struct LinkedList<T>(Option<Arc<Node<T>>>);
558
+ /// struct Node<T>(T, Option<Arc<Node<T>>>);
559
+ ///
560
+ /// // Dropping a long `LinkedList<T>` relying on the destructor of `Arc`
561
+ /// // can cause a stack overflow. To prevent this, we can provide a
562
+ /// // manual `Drop` implementation that does the destruction in a loop:
563
+ /// impl<T> Drop for LinkedList<T> {
564
+ /// fn drop(&mut self) {
565
+ /// let mut x = self.0.take();
566
+ /// while let Some(arc) = x.take() {
567
+ /// Arc::unwrap_or_drop(arc).map(|node| x = node.1);
568
+ /// }
569
+ /// }
570
+ /// }
571
+ ///
572
+ /// // implementation of `new` and `push` omitted
573
+ /// impl<T> LinkedList<T> {
574
+ /// /* ... */
575
+ /// # fn new() -> Self {
576
+ /// # LinkedList(None)
577
+ /// # }
578
+ /// # fn push(&mut self, x: T) {
579
+ /// # self.0 = Some(Arc::new(Node(x, self.0.take())));
580
+ /// # }
581
+ /// }
582
+ ///
583
+ /// // The following code could still cause a stack overflow
584
+ /// // despite the manual `Drop` impl if that `Drop` impl used
585
+ /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::unwrap_or_drop(arc)`.
586
+ /// {
587
+ /// // create a long list and clone it
588
+ /// let mut x = LinkedList::new();
589
+ /// for i in 0..100000 {
590
+ /// x.push(i); // adds i to the front of x
591
+ /// }
592
+ /// let y = x.clone();
593
+ ///
594
+ /// // drop the clones in parallel
595
+ /// let t1 = std::thread::spawn(|| drop(x));
596
+ /// let t2 = std::thread::spawn(|| drop(y));
597
+ /// t1.join().unwrap();
598
+ /// t2.join().unwrap();
599
+ /// }
535
600
/// ```
536
601
#[ inline]
537
602
#[ unstable( feature = "unwrap_or_drop" , issue = "none" ) ] // FIXME: add issue
0 commit comments