Skip to content

Commit 270a9be

Browse files
committed
Merge branch 'more_atomic_types' of https://github.com/Amanieu/rfcs
2 parents bdbe73d + 450afdd commit 270a9be

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

text/0000-integer_atomics.md

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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

Comments
 (0)