std::sync - Rust (original) (raw)

Module sync

1.0.0 · Source

Expand description

Useful synchronization primitives.

§The need for synchronization

Conceptually, a Rust program is a series of operations which will be executed on a computer. The timeline of events happening in the program is consistent with the order of the operations in the code.

Consider the following code, operating on some global static variables:

// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
#![allow(static_mut_refs)]

static mut A: u32 = 0;
static mut B: u32 = 0;
static mut C: u32 = 0;

fn main() {
    unsafe {
        A = 3;
        B = 4;
        A = A + B;
        C = B;
        println!("{A} {B} {C}");
        C = A;
    }
}

It appears as if some variables stored in memory are changed, an addition is performed, result is stored in A and the variable C is modified twice.

When only a single thread is involved, the results are as expected: the line 7 4 4 gets printed.

As for what happens behind the scenes, when optimizations are enabled the final generated machine code might look very different from the code:

The compiler is allowed to perform any combination of these optimizations, as long as the final optimized code, when executed, produces the same results as the one without optimizations.

Due to the concurrency involved in modern computers, assumptions about the program’s execution order are often wrong. Access to global variables can lead to nondeterministic results, even ifcompiler optimizations are disabled, and it is still possibleto introduce synchronization bugs.

Note that thanks to Rust’s safety guarantees, accessing global (static) variables requires unsafe code, assuming we don’t use any of the synchronization primitives in this module.

§Out-of-order execution

Instructions can execute in a different order from the one we define, due to various reasons:

§Higher-level synchronization objects

Most of the low-level synchronization primitives are quite error-prone and inconvenient to use, which is why the standard library also exposes some higher-level synchronization objects.

These abstractions can be built out of lower-level primitives. For efficiency, the sync objects in the standard library are usually implemented with help from the operating system’s kernel, which is able to reschedule the threads while they are blocked on acquiring a lock.

The following is an overview of the available synchronization objects:

atomic

Atomic types

mpsc

Multi-producer, single-consumer FIFO queue communication primitives.

mpmcExperimental

Multi-producer, multi-consumer FIFO queue communication primitives.

poisonExperimental

Synchronization objects that employ poisoning.

Arc

A thread-safe reference-counting pointer. ‘Arc’ stands for ‘Atomically Reference Counted’.

Barrier

A barrier enables multiple threads to synchronize the beginning of some computation.

BarrierWaitResult

A BarrierWaitResult is returned by Barrier::wait() when all threads in the Barrier have rendezvoused.

Condvar

A Condition Variable

LazyLock

A value which is initialized on the first access.

Mutex

A mutual exclusion primitive useful for protecting shared data

MutexGuard

An RAII implementation of a “scoped lock” of a mutex. When this structure is dropped (falls out of scope), the lock will be unlocked.

Once

A low-level synchronization primitive for one-time global execution.

OnceLock

A synchronization primitive which can nominally be written to only once.

OnceState

State yielded to Once::call_once_force()’s closure parameter. The state can be used to query the poison status of the Once.

PoisonError

A type of error which can be returned whenever a lock is acquired.

RwLock

A reader-writer lock

RwLockReadGuard

RAII structure used to release the shared read access of a lock when dropped.

RwLockWriteGuard

RAII structure used to release the exclusive write access of a lock when dropped.

WaitTimeoutResult

A type indicating whether a timed wait on a condition variable returned due to a time out or not.

Weak

Weak is a version of Arc that holds a non-owning reference to the managed allocation.

ExclusiveExperimental

Exclusive provides only mutable access, also referred to as _exclusive_access to the underlying value. It provides no immutable, or _shared_access to the underlying value.

MappedMutexGuardExperimental

An RAII mutex guard returned by MutexGuard::map, which can point to a subfield of the protected data. When this structure is dropped (falls out of scope), the lock will be unlocked.

MappedRwLockReadGuardExperimental

RAII structure used to release the shared read access of a lock when dropped, which can point to a subfield of the protected data.

MappedRwLockWriteGuardExperimental

RAII structure used to release the exclusive write access of a lock when dropped, which can point to a subfield of the protected data.

ReentrantLockExperimental

A re-entrant mutual exclusion lock

ReentrantLockGuardExperimental

An RAII implementation of a “scoped lock” of a re-entrant lock. When this structure is dropped (falls out of scope), the lock will be unlocked.

TryLockError

An enumeration of possible errors associated with a TryLockResult which can occur while trying to acquire a lock, from the try_lock method on aMutex or the try_read and try_write methods on an RwLock.

ONCE_INITDeprecated

Initialization value for static Once values.

LockResult

A type alias for the result of a lock method which can be poisoned.

TryLockResult

A type alias for the result of a nonblocking locking method.