Operațiuni Atomice și Ordinea Memoriei în Rust: AtomicBool, Ordering::Acquire și Release în Rust

Ghid complet pentru programare concurentă fără blocaje (Lock-Free) în Rust: tipurile atomice din std::sync::atomic, înțelegerea modelelor de memorie Relaxed, Acquire, Release și SeqCst, și implementarea unui SpinLock.

Sintaxă
val.store(true, Ordering::Release) | val.load(Ordering::Acquire) | compare_exchange

În timp ce primitivele grele de sincronizare precum Mutex sau RwLock apelează la apeluri de sistem (syscalls) pentru a suspenda firele în nucleul sistemului de operare, operațiunile atomice (Atomics) din modulul std::sync::atomic operează direct la nivel de instrucțiuni de procesor (CPU hardware instructions), oferind sincronizare concurentă de ultra-înaltă viteză fără blocaje (Lock-Free).

Fiecare operațiune atomică solicită un parametru std::sync::atomic::Ordering, care specifică compilatorului și procesorului ce optimizări de reordonare a instrucțiunilor sunt permise în jurul acelei linii de cod.

Implementarea unui SpinLock Simplu cu AtomicBool și Semantica Acquire/Release
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;

pub struct SpinLock {
    blocat: AtomicBool,
}

impl SpinLock {
    pub const fn new() -> Self {
        SpinLock {
            blocat: AtomicBool::new(false),
        }
    }

    pub fn lock(&self) {
        // Încercăm să schimbăm atomica din false în true.
        // Folosim Ordering::Acquire: nicio citire sau scriere viitoare din secțiunea critică 
        // nu poate fi reordonată înaintea acestei linii!
        while self.blocat.swap(true, Ordering::Acquire) {
            // Cedează cuanta CPU pentru a evita consumul inutil de energie:
            std::hint::spin_loop();
        }
    }

    pub fn unlock(&self) {
        // Folosim Ordering::Release: toate modificările efectuate în secțiunea critică
        // sunt garantate să devină vizibile altor fire înainte ca valoarea să devină false!
        self.blocat.store(false, Ordering::Release);
    }
}

fn main() {
    let lock = Arc::new(SpinLock::new());
    let lock_clone = Arc::clone(&lock);

    let handle = thread::spawn(move || {
        lock_clone.lock();
        println!("Firul 2 a obținut blocajul atomic!");
        lock_clone.unlock();
    });

    lock.lock();
    println!("Firul 1 se află în secțiunea critică.");
    lock.unlock();

    handle.join().unwrap();
}

Clasificarea Nivelurilor de Ordine a Memoriei (Memory Ordering)

Nivel de Ordonare Garanții de Sincronizare Hardware Cost / Penalizare de Performanță
Relaxed Garantează doar atomicitatea operației pe acea variabilă. Permite reordonarea liberă a altor instrucțiuni din jur. Minim (instrucțiune nativă fără bariere de memorie).
Release Barieră de scriere: nicio operație anterioară nu poate fi reordonată după această scriere. Scăzut spre mediu (folosit la publicarea de date).
Acquire Barieră de citire: nicio operație viitoare nu poate fi reordonată înaintea acestei citiri. Scăzut spre mediu (folosit la consumarea de date).
SeqCst Sequential Consistency: garantează o ordine globală strictă observabilă identic de către toate nucleele CPU. Maxim (inserează instrucțiuni grele de barieră de memorie hardware – MFENCE).