kmky
kmky

Reputation: 801

How to implement AtomicU32, an atomic 32-bit integer type?

The module std::sync::atomic doesn't contain fixed size integral atomic types. In my 64-bit application, I need to create a vector of atomic 32-bit integers. Is there a workaround? Can I implement something like an AtomicU32 (or AtomicI32) myself, and how?

Upvotes: 3

Views: 1450

Answers (2)

Francis Gagné
Francis Gagné

Reputation: 65832

The std::intrinsics module contains intrinsic atomic functions that are generic. This means you can basically copy-paste the implementation for AtomicUsize and/or AtomicIsize and change the wrapped type from usize/isize to u32/i32.

However, these intrinsics are unstable, so you need to use a nightly Rust compiler in order to make this work. If that's not acceptable to you, consider submitting an RFC to get such types added to the standard library. If using a nightly Rust compiler is OK for you, then it might be interesting to publish a crate that contains these types. This will have the advantage of gathering statistics on how useful such types are. (Dependents will still need to use a nightly compiler though, so the effect might be somewhat underestimated.)

Upvotes: 4

oli_obk
oli_obk

Reputation: 31263

I don't think you can implement other sizes than pointer-sized integers. AtomicBool is actually padded in Rust to pointer size.

LLVM states that

Common architectures have some way of representing at least a pointer-sized lock-free cmpxchg; such an operation can be used to implement all the other atomic operations which can be represented in IR up to that size.

Therefor you will never save space with smaller integers.

On the other hand, if you just want nice access and don't care about the memory, you can implement wrappers:

use std::sync::atomic::{AtomicIsize, Ordering};

struct AtomicI32(AtomicIsize);
impl AtomicI32 {
    fn new(v: i32) -> AtomicI32 {
        AtomicI32(AtomicIsize::new(v as isize))
    }
    fn load(&self, order: Ordering) -> i32 {
        self.0.load(order) as i32
    }
    fn store(&self, val: i32, order: Ordering) {
        self.0.store(val as isize, order)
    }
    fn swap(&self, val: i32, order: Ordering) -> i32 {
        self.0.swap(val as isize, order) as i32
    }
    fn compare_and_swap(&self, current: i32, new: i32, order: Ordering) -> i32 {
        self.0.compare_and_swap(current as isize, new as isize, order) as i32
    }
    fn fetch_add(&self, val: i32, order: Ordering) -> i32 {
        self.0.fetch_add(val as isize, order) as i32
    }
    fn fetch_sub(&self, val: i32, order: Ordering) -> i32 {
        self.0.fetch_sub(val as isize, order) as i32
    }
    fn fetch_and(&self, val: i32, order: Ordering) -> i32 {
        self.0.fetch_and(val as isize, order) as i32
    }
    fn fetch_or(&self, val: i32, order: Ordering) -> i32 {
        self.0.fetch_or(val as isize, order) as i32
    }
    fn fetch_xor(&self, val: i32, order: Ordering) -> i32 {
        self.0.fetch_xor(val as isize, order) as i32
    }
}

Upvotes: 2

Related Questions