|
| 1 | +- Feature Name: integer_atomics |
| 2 | +- Start Date: 2016-03-14 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +This RFC basically changes `core::sync::atomic` to look like this: |
| 10 | + |
| 11 | +```rust |
| 12 | +#[cfg(target_has_atomic = "8")] |
| 13 | +struct AtomicBool {} |
| 14 | +#[cfg(target_has_atomic = "8")] |
| 15 | +struct AtomicI8 {} |
| 16 | +#[cfg(target_has_atomic = "8")] |
| 17 | +struct AtomicU8 {} |
| 18 | +#[cfg(target_has_atomic = "16")] |
| 19 | +struct AtomicI16 {} |
| 20 | +#[cfg(target_has_atomic = "16")] |
| 21 | +struct AtomicU16 {} |
| 22 | +#[cfg(target_has_atomic = "32")] |
| 23 | +struct AtomicI32 {} |
| 24 | +#[cfg(target_has_atomic = "32")] |
| 25 | +struct AtomicU32 {} |
| 26 | +#[cfg(target_has_atomic = "64")] |
| 27 | +struct AtomicI64 {} |
| 28 | +#[cfg(target_has_atomic = "64")] |
| 29 | +struct AtomicU64 {} |
| 30 | +#[cfg(target_has_atomic = "128")] |
| 31 | +struct AtomicI128 {} |
| 32 | +#[cfg(target_has_atomic = "128")] |
| 33 | +struct AtomicU128 {} |
| 34 | +#[cfg(target_has_atomic = "ptr")] |
| 35 | +struct AtomicIsize {} |
| 36 | +#[cfg(target_has_atomic = "ptr")] |
| 37 | +struct AtomicUsize {} |
| 38 | +#[cfg(target_has_atomic = "ptr")] |
| 39 | +struct AtomicPtr<T> {} |
| 40 | +``` |
| 41 | + |
| 42 | +# Motivation |
| 43 | +[motivation]: #motivation |
| 44 | + |
| 45 | +Many lock-free algorithms require a two-value `compare_exchange`, which is effectively twice the size of a `usize`. This would be implemented by atomically swapping a struct containing two members. |
| 46 | + |
| 47 | +Another use case is to support Linux's futex API. This API is based on atomic `i32` variables, which currently aren't available on x86_64 because `AtomicIsize` is 64-bit. |
| 48 | + |
| 49 | +# Detailed design |
| 50 | +[design]: #detailed-design |
| 51 | + |
| 52 | +## New atomic types |
| 53 | + |
| 54 | +The `AtomicI8`, `AtomicI16`, `AtomicI32`, `AtomicI64` and `AtomicI128` types are added along with their matching `AtomicU*` type. These have the same API as the existing `AtomicIsize` and `AtomicUsize` types. Note that support for 128-bit atomics is dependent on the [i128/u128 RFC](https://github.com/rust-lang/rfcs/pull/1504) being accepted. |
| 55 | + |
| 56 | +## Target support |
| 57 | + |
| 58 | +One problem is that it is hard for a user to determine if a certain type `T` can be placed inside an `Atomic<T>`. After a quick survey of the LLVM and Clang code, architectures can be classified into 3 categories: |
| 59 | + |
| 60 | +- The architecture does not support any form of atomics (mainly microcontroller architectures). |
| 61 | +- The architecture supports all atomic operations for integers from i8 to iN (where N is the architecture word/pointer size). |
| 62 | +- The architecture supports all atomic operations for integers from i8 to i(N*2). |
| 63 | + |
| 64 | +A new target cfg is added: `target_has_atomic`. It will have multiple values, one for each atomic size supported by the target. For example: |
| 65 | + |
| 66 | +```rust |
| 67 | +#[cfg(target_has_atomic = "128")] |
| 68 | +static ATOMIC: AtomicU128 = AtomicU128::new(mem::transmute((0u64, 0u64))); |
| 69 | +#[cfg(not(target_has_atomic = "128"))] |
| 70 | +static ATOMIC: Mutex<(u64, u64)> = Mutex::new((0, 0)); |
| 71 | + |
| 72 | +#[cfg(target_has_atomic = "64")] |
| 73 | +static COUNTER: AtomicU64 = AtomicU64::new(0); |
| 74 | +#[cfg(not(target_has_atomic = "64"))] |
| 75 | +static COUTNER: AtomicU32 = AtomicU32::new(0); |
| 76 | +``` |
| 77 | + |
| 78 | +Note that it is not necessary for an architecture to natively support atomic operations for all sizes (`i8`, `i16`, etc) as long as it is able to perform a `compare_exchange` operation with a larger size. All smaller operations can be emulated using that. For example a byte atomic can be emulated by using a `compare_exchange` loop that only modifies a single byte of the value. This is actually how LLVM implements byte-level atomics on MIPS, which only supports word-sized atomics native. Note that the out-of-bounds read is fine here because atomics are aligned and will never cross a page boundary. Since this transformation is performed transparently by LLVM, we do not need to do any extra work to support this. |
| 79 | + |
| 80 | +## Changes to `AtomicPtr`, `AtomicIsize` and `AtomicUsize` |
| 81 | + |
| 82 | +These types will have a `#[cfg(target_has_atomic = "ptr")]` bound added to them. Although these types are stable, this isn't a breaking change because all targets currently supported by Rust will have this type available. This would only affect custom targets, which currently fail to link due to missing compiler-rt symbols anyways. |
| 83 | + |
| 84 | +## Changes to `AtomicBool` |
| 85 | + |
| 86 | +This type will be changes to use an `AtomicU8` internally instead of an `AtomicUsize`, which will allow it to be safely transmuted to a `bool`. This will make it more consistent with the other atomic types that have the same layout as their underlying type. (For example futex code will assume that a `&AtomicI32` can be passed as a `&i32` to the system call) |
| 87 | + |
| 88 | +# Drawbacks |
| 89 | +[drawbacks]: #drawbacks |
| 90 | + |
| 91 | +Having certain atomic types get enabled/disable based on the target isn't very nice, but it's unavoidable because support for atomic operations is very architecture-specific. |
| 92 | + |
| 93 | +This approach doesn't directly support for atomic operations on user-defined structs, but this can be emulated using transmutes. |
| 94 | + |
| 95 | +# Alternatives |
| 96 | +[alternatives]: #alternatives |
| 97 | + |
| 98 | +One alternative that was discussed in a [previous RFC](https://github.com/rust-lang/rfcs/pull/1505) was to add a generic `Atomic<T>` type. However the consensus was that having unsupported atomic types either fail at monomorphization time or fall back to lock-based implementations was undesirable. |
| 99 | + |
| 100 | +Several other designs have been suggested [here](https://internals.rust-lang.org/t/pre-rfc-extended-atomic-types/3068). |
| 101 | + |
| 102 | +# Unresolved questions |
| 103 | +[unresolved]: #unresolved-questions |
| 104 | + |
| 105 | +None |
0 commit comments