Create Arc if Weak::upgrade return None

Is there a way to create an Arc if Weak::upgrade return None? Something like Weak::upgrade_or_create?

For example

fn do_something(weak: &Weak<u32>) {
    let arc: Arc<u32> = weak.upgrade_or_insert(Arc::new(42));
    // do something ...
}

Notice that I cannot just create an arc and downgrade it and assign to weak:

fn do_something(weak:  &Weak<u32>) {
    let arc: Arc<u32> = match weak.upgrade() {
        None => {
            let arc = Arc::new(42);
            *weak = Arc::downgrade(&arc); // will not work, since weak is not mutable
            arc
        }
        Some(arc) => { arc }
    };
    // do something ...
}

because the weak is not mutable.

P.S. Please correct me if I'm wrong, I think a function like upgrade_or_insert work on an immutable weak is totally safe, since the newly created Arc do not get overlap memory writing problem.

It's not clear what exact semantics you are expecting. You don't need to go back and forth between Arc and Weak, nor do you need a mutable Weak for this. Just write

let arc = weak.upgrade().unwrap_or_else(|| Arc::new(42));

Hi, I want to make the weak points to a valid Arc as well. I think my second example illustrate that (the code maybe buggy, I updated the code).

If you only have an immutable reference to Weak, then it's not possible for you to change it to point to your new Arc. You would need a mutable reference to do that, and in that case the match you posted will work.

2 Likes

The question is not about the example, it's about whether a function Weak<T>::upgrade_or_insert(&self, f: FnOnce() -> Arc<T>) make sense or not...

You can't implement it because calling it on the same Weak from two threads in parallel would be a data race. It would require a mutable reference.

2 Likes

If you’re proposing adding such a method to the standard library, you’ll want to open a thread on IRLO instead of here. You’ll need a design that avoids the data race @alice mentioned, though.

1 Like

That's the key point, if I just assign a brand new valid ArcInner to ptr: NonNull<ArcInner<T>> after checking, will it cause a problem?

Or if you say assign operation is not guaranteed to be atomic, what if I wrap NonNull<ArcInner<T>> to an atomicptr?

Mutable reference is not enough as there can be multiple Weaks pointing to same allocation. To ensure the replaced value is stored some kind of locking mechanism is needed.

It seems like you are making the mistake discussed here at 20:46. The ptr field in an Arc (or Weak) is not atomic, and the standard library is not going to change it to be an atomic.

I assumed that only the weak it was called on would be updated, and not other clones of the same weak. It's possible that I misunderstood.

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.