Increase refcount of a raw Arc pointer?

I'm giving an Arc::into_raw() pointer to C. Later, when I get the raw pointer back, I need to clone the Arc to increase its refcount and get an owned copy. What's the right way to do this?

Arc<T> is nice enough that a raw pointer it gives is *const T. However, this means that dereference of that pointer is not the Arc any more, so (*ptr).clone() is not the Arc::clone.

I came up with this, but it looks awkward:

let arc = unsafe {
    let tmp = Arc::from_raw(pointer);
    let arc = Arc::clone(&tmp);
    let _ = Arc::into_raw(tmp);
    arc
};

Is there a more elegant way?

Personally I prefer to mem::forget(Arc::clone(&arc))

2 Likes

You mean:

let arc = Arc::from_raw(ptr);
mem::forget(Arc::clone(&arc));

?

Yes, that's how you would go from a *const T to increasing the ref count of the Arc by 1.

that's the way to do it, and I would keep it that way; if it looks awkward, just factor that out:

/// # Safety
///
///   - `raw` must come from an `Arc::into_raw()` call.
#[inline]
unsafe
fn arc_clone_from_raw<T : ?Sized> (raw: *const T) -> Arc<T>
{
    Arc::clone(
        &*::core::mem::ManuallyDrop::new( // alternative to Arc::into_raw / mem::forget
            Arc::from_raw(raw)
        )
    )
}
1 Like

Why don't you clone the Arc when initially passing the pointer to C? i.e. not trying to bump the reference count after the fact.

That way you're creating a copy (bumping the reference count) then passing a raw pointer to some other code. At some time later on the C code will invoke a destructor function that would call Arc::from_raw() and immediately let the Arc go out of scope, decrementing the reference count again.

Yeah, initially I already return an object with refcount=1. However, in this case I'm also spawning a thread, which the C side technically doesn't know about, so it could tell me to free the whole object before my thread has finished. Plus, it's just safer to use safe constructs instead of trying to get away with raw pointers.

(I've tried a design that doesn't spawn hidden threads in Rust, and made the C side to deal with threading, but C programmers really didn't like that).