Skip to content

Lock-free compare-and-swap-ey box-y things. #2322

@SoniEx2

Description

@SoniEx2

Summary

AtomicCell<T> provides a cell-like box-like lock-free mechanism for shared objects.

Motivation

Rust has the following synchronization... things:

  • Mutex<T>
  • AtomicPtr<T>
  • Maybe a few other things (RwLock<T>? who uses that? :p)

But besides AtomicPtr<T>, which is unsafe, there's nothing that can do compare-and-swap-ey lock-free operations on stuff.

Guide

AtomicCell<T> provides a way to work with/protect shared data without locking. This is in contrast to Mutex<T>, which locks the data to a specific thread.

[TODO not described]

Implementation

An AtomicCell<T> is vaguely similar to a Cell<T> or a Mutex<T>, and much closer to a Box<T>. It provides (at least) the following methods:

fn new(start_value: T) -> AtomicCell<T>;
fn swap(&self, other: &AtomicCell<T> /* perhaps an Ordering? */) /* -> bool ?*/;
fn get(&self /* perhaps an Ordering? */) -> T where T: Copy;
fn set(&self, val: T /* perhaps an Ordering? */);
fn replace(&self, val: T /* perhaps an Ordering? */) -> T;

It's mainly a safe wrapper around AtomicPtr<T>, and swap is more efficient than replace.

This efficiency difference boils down to heap allocations: AtomicCell<T> is similar to Box<T>, which means it has to allocate T in memory. When you have two AtomicCell<T>s, swap can simply swap their pointers, while with replace you first need to allocate heap memory for the new value, swap that pointer, and then read the resulting pointer.

Drawbacks

  1. This lacks a compare-and-swap mechanism. I couldn't figure this one out.
  2. Like any additions to the standard library, this increases its complexity, which implies an increase in maintenance costs (code, documentation, tutorials).

Rationale and Alternatives

One could think of adding swap to Mutex<T>. However, Mutex<T> stores the data in the structure itself, not on the heap, which means you can't use compare-and-swap/lock-free operations on it.

One alternative is to add swapping straight to Arc<Mutex<T>> instead. This seems rather awkward tho.

Unresolved questions

Currently:

  • Should we use Ordering?
    • I don't know how Ordering works.
  • Is there any way to add a zero-cost compare-and-swap mechanism, or should we defer the cost to the user?
    • Mainly, if the type is bigger than the hardware's compare-and-swap instruction, or if the Eq/PartialEq isn't a raw memory equality, it gets really tricky really fast.
  • Do the benefits outweigh the maintenance costs?
  • How do we explain this to new users? Can we explain it differently to experienced users?

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiRelevant to the library API team, which will review and decide on the RFC.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions