Skip to content
CryoCryo home
Stdliballoc

arc

import std::alloc::arc; · source

Types

Arc<T, A>

type struct Arc<T, A = GlobalAlloc> where A: Allocator {
    inner: ArcInner<T, A>*;

    static new(value: T) -> Arc<T, GlobalAlloc>;
    static new_in(value: T, alloc: A) -> Arc<T, A>;
    static try_new(value: T) -> Result<Arc<T, GlobalAlloc>, AllocError>;
    static try_new_in(value: T, alloc: A) -> Result<Arc<T, A>, AllocError>;
    clone(&this) -> Arc<T, A>;
    downgrade(&this) -> Weak<T, A>;
    strong_count(&this) -> u64;
    weak_count(&this) -> u64;
    as_ptr(&this) -> T*;
    get_ref(&this) -> T*;
    get_mut(mut &this) -> Option<T*>;
    into_raw(mut this) -> ArcInner<T, A>*;
    static from_raw(p: ArcInner<T, A>*) -> Arc<T, A>;
    static increment_strong_count(p: ArcInner<T, A>*) -> void;

    static decrement_strong_count(p: ArcInner<T, A>*) -> void
    where A: Allocator;
}

Shared ownership across threads. Arc is the thread-safe sibling of Rc. Same shape, same two-phase teardown, but the counts are Atomic<u64>, so handles can be cloned and dropped concurrently without a data race. It is structurally Send + Sync whenever T and A are.

import std::alloc::arc;
import std::thread;

mut shared: Arc<Table> = Arc::<Table>::new(table);
mut copy: Arc<Table> = shared.clone();
thread::spawn(worker, copy);

The API mirrors Rc — new, new_in, try_new, try_new_in, clone, downgrade, get_ref, get_mut, strong_count, weak_count — with upgrade on Weak running a compare-exchange loop so it can never resurrect a value whose strong count already reached zero.

get_mut requires strong == 1 && weak == 1. With a single strong reference no other thread holds an Arc to clone or downgrade from, so once both counts read 1 the access really is exclusive. Even then, interior mutation visible across threads needs the value itself to be Sync — an atomic, say.

Properties

PropertyType
innerArcInner<T, A>*

Constructors

static new(value: T) -> Arc<T, GlobalAlloc>;

Allocate a new Arc backed by GlobalAlloc with an initial count of 1. Panics on allocation failure; use try_new.

static new_in(value: T, alloc: A) -> Arc<T, A>;

Allocate with a caller-supplied allocator. Panics on failure.

static try_new(value: T) -> Result<Arc<T, GlobalAlloc>, AllocError>;

Fallible constructor backed by GlobalAlloc. Returns Err(AllocError) if the allocator refuses the request.

static try_new_in(value: T, alloc: A) -> Result<Arc<T, A>, AllocError>;

Fallible constructor with a caller-supplied allocator. The allocator instance is stored in the inner header and is used to free the inner when the last reference drops.

static from_raw(p: ArcInner<T, A>*) -> Arc<T, A>;

Reconstitute a handle from a pointer returned by into_raw, taking back ownership of exactly one strong reference; the returned handle's drop balances it. Undefined unless p came from into_raw (or an increment_strong_count) on an Arc<T, A> of this exact type whose reference has not already been balanced.

Static functions

static increment_strong_count(p: ArcInner<T, A>*) -> void;

Add one to the strong count of an into_raw'd header held only as a raw pointer (no live handle). Pairs one-for-one with a later decrement_strong_count. Relaxed matches clone: the new owner already shares a happens-before edge with the caller.

static decrement_strong_count(p: ArcInner<T, A>*) -> void where A: Allocator;

Remove one strong reference from a header held only as a raw pointer, running the value's destructor and freeing the header if it was the last. Balances one into_raw or increment_strong_count by routing through the handle Drop, so the release/acquire teardown ordering is identical. Undefined if the reference was already balanced.

Methods

clone(&this) -> Arc<T, A>;

Produce another Arc pointing at the same value. The two handles are interchangeable; the strong count is incremented atomically with Relaxed ordering - the canonical reference-count pattern: the new owner already shares a happens-before with the cloning thread, so no further synchronization is needed at clone time.

downgrade(&this) -> Weak<T, A>;

A non-owning Weak handle to the same value. Bumps the weak count (Relaxed suffices: the new Weak shares a happens-before with this thread already) but not the strong count.

strong_count(&this) -> u64;

Current strong-reference count. Reads the atomic with SeqCst; treat this as advisory - by the time the caller uses the value another thread may have changed it.

weak_count(&this) -> u64;

Number of live Weak handles (advisory). The strong group's shared unit is excluded, so this is 0 when no Weak has been created.

as_ptr(&this) -> T*;

Raw pointer to the shared value. Valid for as long as any strong clone of this Arc is still alive; dereferencing after the last drop is undefined.

get_ref(&this) -> T*;

Shared borrow of the value. Sound for reads while any strong clone is alive. For mutation use get_mut (gated on uniqueness); note that even with unique ownership, interior mutation visible across threads needs the value itself to be Sync (e.g. an atomic).

get_mut(mut &this) -> Option<T*>;

Mutable borrow only when this is the sole owner: exactly one strong reference AND no outstanding Weak (strong == 1 && weak == 1, both read with Acquire). With a single strong reference no other thread holds an Arc to clone or downgrade from, so once both counts read 1 the access is exclusive; otherwise None.

into_raw(mut this) -> ArcInner<T, A>*;

Consume this handle without touching the strong count, yielding the raw header it referenced. The caller inherits the one strong reference this handle held and MUST later balance it - recover a handle with from_raw and drop it, or call decrement_strong_count - or the value leaks. The consumed handle's own drop is suppressed (its inner is nulled), so yielding the pointer here is not itself a decrement. Enables a raw ArcInner* to be parked in a void* slot (e.g. a manual-vtable waker) outside the type system while still owning a reference.

Weak<T, A>

type struct Weak<T, A = GlobalAlloc> {
    inner: ArcInner<T, A>*;

    static new() -> Weak<T, GlobalAlloc>;
    upgrade(&this) -> Option<Arc<T, A>>;
    clone(&this) -> Weak<T, A>;
    strong_count(&this) -> u64;
}

The same shape as rc::Weak: it does not keep the value alive, upgrade returns None once the last strong handle is gone, and Weak::new() is the placeholder that never upgrades. The difference is that upgrade here is a compare-exchange loop, so it can never resurrect a value whose strong count another thread has already taken to zero.

Properties

PropertyType
innerArcInner<T, A>*

Constructors

static new() -> Weak<T, GlobalAlloc>;

A Weak that never upgrades. Allocates nothing; upgrade always returns None.

Methods

upgrade(&this) -> Option<Arc<T, A>>;

Try to obtain a strong Arc. Returns None once the last strong reference has gone, or for a dangling Weak::new().

clone(&this) -> Weak<T, A>;

Another Weak to the same value. Bumps the weak count.

strong_count(&this) -> u64;

Live strong count, or 0 for a dangling Weak. Advisory.

The raw-pointer escape hatch

For parking a reference outside the type system — a manual-vtable waker, a C callback's void* — four statics operate on the header pointer directly:

  • into_raw consumes a handle without touching the count.
  • from_raw takes the reference back as a handle.
  • increment_strong_count adds one to a header held only as a raw pointer, as clone would.
  • decrement_strong_count removes one, running the destructor and freeing the header if it was the last — as drop would.

Each into_raw or increment_strong_count must be balanced exactly once by from_raw or decrement_strong_count, or the value leaks. Unbalancing in the other direction is undefined.

The memory ordering, briefly

type struct ArcInner<T, A> {
    strong: Atomic<u64>;
    weak:   Atomic<u64>;
    value:  T;
    alloc:  A;
}

Clone bumps the count with Relaxed; drop decrements with Release and the last-decrement path issues an Acquire fence. This is the canonical Boost/Rust pattern. Relaxed is right for the clone because the new reference is only ever observed by callers who already share a happens-before edge with the cloning thread. The release/acquire pair on drop is what synchronizes the final T::drop() against every prior mutation from any thread that has already dropped its own handle.

Routing every free decision through the one atomic weak counter is what makes the race between "the last Arc drops" and "a Weak drops" safe: there is never a moment where two threads read two separate counters and both decide to free.

Properties
PropertyType
strongAtomic<u64>
weakAtomic<u64>
valueT
allocA

Trait implementations

Arc

implement<T, A> trait Drop for struct Arc<T, A> where A: Allocator

Weak

implement<T, A> trait Drop for struct Weak<T, A> where A: Allocator