Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAdd more integer atomic types #1543
Conversation
This comment has been minimized.
This comment has been minimized.
comex
commented
Mar 15, 2016
|
The rendered link is wrong. |
This comment has been minimized.
This comment has been minimized.
Fixed |
This comment has been minimized.
This comment has been minimized.
|
Thanks for writing this up @Amanieu! I'm personally would prefer to take this strategy over #1505 as it's more faithful to the atomics we have today (in terms of implementation strategy). There are some small tweaks, however, that I might also change:
|
This comment has been minimized.
This comment has been minimized.
jmesmon
commented
Mar 15, 2016
|
Regarding
Are you asking for something like |
This comment has been minimized.
This comment has been minimized.
|
I was persuaded by @Amanieu's comment that there's so much variance within one architecture (e.g. ARM) that we shouldn't have something that is tied to the architecture but rather support via |
This comment has been minimized.
This comment has been minimized.
I believe this is addressed in the "Target support" section.
The reason for this was to support
I don't really mind, there are plenty of opportunities for bikeshedding here.
In my view
As I said above, my idea is that libcore supports all platforms, whereas libstd requires a certain minimal feature set to function. |
This comment has been minimized.
This comment has been minimized.
jmesmon
commented
Mar 16, 2016
|
@alexcrichton so |
This comment has been minimized.
This comment has been minimized.
|
Ah sorry looks like I missed that part of the RFC, my mistake! I wonder though if the
Yeah in some sense this is true, but platforms supporting pointer-sized atomics seems very common where supporting, for example, 64-bit atomics is much less common. I don't think we'd want usage of
Ah yes, thanks for pointing this out again! My opinion is that Yeah something along those lines (although the names I'd certainly be willing to debate). |
alexcrichton
self-assigned this
Mar 16, 2016
alexcrichton
added
the
T-libs
label
Mar 16, 2016
alexcrichton
referenced this pull request
Mar 21, 2016
Closed
Make AtomicBool the same size as bool #32365
This comment has been minimized.
This comment has been minimized.
|
Yesterday the libs team was discussing rust-lang/rust#32365 and an interesting point came up. @brson remembered an article in the past which outlined how bitfields can cause problems in C with respect to atomics and word sizes. This is mostly connected to bitfields I think, but it's an interesting data point about smaller-than-word-size atomics using word-size instructions with compare-and-swap to emulate an atomic operation. My digestion of that article, however, is that it doesn't likely impact Rust for now, as we don't have bitfields. Additionally, if we did have bitfields, it seems like we probably wouldn't want those semantics for other reasons? |
This comment has been minimized.
This comment has been minimized.
jmesmon
commented
Mar 24, 2016
|
That gcc bug ( https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52080 ) was that a larger than required read-modify-write was used, which resulted in a non-atomic read-modify-write of a field marked volatile in a struct. It happened to trigger due to some bad handling of bitfields, but the basic concept still exists without them: imagine a machine that can't load/store less than 32bits at a time. In the article, most of the issue seems to have come from C not having completely specified behavior (ie: the C standard current allows a bit too much flexibility) for I suppose our answer to this would mostly hinge on:
|
This comment has been minimized.
This comment has been minimized.
I think this bug was a case of gcc specifically violating the C++ memory model which forbids a store to one memory location from affecting another memory location. This is explained in this comment which comes from the fix for that gcc bug. /* In the C++ memory model, consecutive bit fields in a structure are
considered one memory location and updating a memory location
may not store into adjacent memory locations. */An architecture that is unable to generate byte-level stores is fundamentally incompatible with the C++ memory model, and would therefore not be supported by Rust since we depend on that memory model. The only way that an architecture which only has 32-bit stores could be supported in C would be to make |
This comment has been minimized.
This comment has been minimized.
This isn't actually a problem in this case because the compare-and-swap is atomic, which means that another thread reading data near the emulated atomic will never see an incorrect value and will never have a write which races with the atomic write. This is why this transformation performed by LLVM is valid and does not violate the C++ memory model. The only problematic situation is when a non-atomic read-modify-write operation is performed because then a store by a second thread could be "lost" if it occurs while another thread is accessing nearby data. The good news is that all architectures that support the C++ memory model must support byte-level stores, therefore having Rust rely on this feature is perfectly fine. |
This comment has been minimized.
This comment has been minimized.
|
Here is a rough idea of what the implementation of a generic struct Atomic<T>(UnsafeCell<T>);
impl<T: Copy> Atomic<T> {
pub fn swap(&self, val: T, order: Ordering) -> T {
#[cfg(target_has_atomic = "128")]
if mem::size_of::<T>() == 16 && mem::align_of::<T>() >= 16 {
let a = &*(self as *const Atomic<T> as *const AtomicU128);
return mem::transmute(a.swap(mem::transmute(val), order));
}
#[cfg(target_has_atomic = "64")]
if mem::size_of::<T>() == 8 && mem::align_of::<T>() >= 8 {
let a = &*(self as *const Atomic<T> as *const AtomicU64);
return mem::transmute(a.swap(mem::transmute(val), order));
}
#[cfg(target_has_atomic = "32")]
if mem::size_of::<T>() == 4 && mem::align_of::<T>() >= 4 {
let a = &*(self as *const Atomic<T> as *const AtomicU32);
return mem::transmute(a.swap(mem::transmute(val), order));
}
#[cfg(target_has_atomic = "16")]
if mem::size_of::<T>() == 2 && mem::align_of::<T>() >= 2 {
let a = &*(self as *const Atomic<T> as *const AtomicU16);
return mem::transmute(a.swap(mem::transmute(val), order));
}
#[cfg(target_has_atomic = "8")]
if mem::size_of::<T>() == 1 {
let a = &*(self as *const Atomic<T> as *const AtomicU8);
return mem::transmute(a.swap(mem::transmute(val), order));
}
let result: T = mem::uninitialized();
atomic_swap_fallback(self.0.get() as *mut (), &val as *const (), &mut result as *mut (), mem::size_of::<T>());
result
}
} |
This comment has been minimized.
This comment has been minimized.
|
I just released a crate which uses this to implement a generic |
alexcrichton
added
the
I-nominated
label
Mar 30, 2016
This comment has been minimized.
This comment has been minimized.
|
Nice work @Amanieu! I'm gonna push for this to move into FCP this week hopefully |
This comment has been minimized.
This comment has been minimized.
|
|
alexcrichton
added
final-comment-period
and removed
I-nominated
labels
Mar 31, 2016
This comment has been minimized.
This comment has been minimized.
|
I personally am in favor of this RFC as an approach to handling differently sized atomics. In terms of technical details, I've got two pretty minor reservations:
I'd personally propose |
This comment has been minimized.
This comment has been minimized.
The RFC states that 128-bit atomics depend on the
This still leaves the issue of what we should do about the existing atomic types on platforms that do not support any form of atomics. |
This comment has been minimized.
This comment has been minimized.
jmesmon
commented
Mar 31, 2016
I don't think separate modules necessarily adds value for users of this API (while it may save some extra It also isn't clear how things like Placing atomics in |
This comment has been minimized.
This comment has been minimized.
|
Ah my mistake! Double-word compare and swaps, however, are pretty useful so I hear, so maybe we could get u64x2 today and maybe deprecate it in the future if u128 is stabilized?
Indeed! I should clarify my thinking there. I'm not really sure what the best course of action is here, but as I've said before I think our hands are basically tied by today's stabilization. I don't think we want to deprecate We can probably just bolster the documentation to indicate that platforms which don't have
It's essentially the same strategy as Similarly, with these atomics we'd basically just be indicating which platforms have support for these kinds of atomic operations. Only if the target indeed has For |
This comment has been minimized.
This comment has been minimized.
jmesmon
commented
Mar 31, 2016
No, I couldn't. Nothing about With
No, it doesn't entirely clarify that. Are you saying that |
This comment has been minimized.
This comment has been minimized.
|
Yes, that's what I'd be thinking ( |
aturon
referenced this pull request
Apr 4, 2016
Closed
Add target_has_floating_point property and make floats optional in libcore #32651
This comment has been minimized.
This comment has been minimized.
|
I wanted to cross-post a comment I just wrote elsewhere, related to APIs whose existence is platform/arch dependent. I just wanted to make a general point that we have a strong need for vision in the area of arch/OS/config/...-dependent APIs. Examples include:
The common thread among all these problems is that we want to expose, in libraries like But this clearly doesn't scale to other kinds of distinctions. Now, it's not obvious that all of the above examples can be solved by a single mechanism. But I am really eager to see some basic vision for how to approach these questions in Rust, before we go too far down the road of adding specific ad hoc APIs. If @Amanieu or anyone else is interested in this topic in general, I'd love to work together toward an RFC! That said, @alexcrichton has been making some concrete suggestions along these lines for the current RFC (via |
This comment has been minimized.
This comment has been minimized.
eternaleye
commented
Apr 4, 2016
|
@aturon: One thing to consider - module hierarchies are a strict tree, but some functionality may well have fan-in, not just fan-out. As an example, an atomic float type would depend on both 32-bit atomics and float support - and one does not necessarily imply the other. This can be hacked around with As the first example that comes to mind, RISC-V has separate extensions for floating-point ( In fact, it's very likely that the minion cores on lowRISC will need atomics, and very unlikely they'll need floating-point - and as they'll be largely dedicated to being isolated coprocessors for running drivers, Rust's safety guarantees are very relevant. Similarly, DSPs are very likely to want floating point, but less likely to need atomics - and Rust's support for zero-cost abstractions would make it much easier to develop for them without sacrificing performance. As a result, I think it's probably most useful to have a namespace whose members are platform dependent, but not use the names to distinguish platform. They just don't match the non-hierarchical reality. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
I think it is important to consider how these examples differ from each other:
(I'm not commenting on the Android ABI issue since I'm not familiar with it) In conclusion, I don't think there is much of a case for a common mechanism to handle all these cases. I think a namespace like |
This comment has been minimized.
This comment has been minimized.
|
@Amanieu Thanks -- I agree that there are substantial differences here. What I'm most interested in having a discussion about is what our overall goals are in terms of platform support/compatibility (which may vary in each of the cases we're discussing), and then the best mechanisms to support those goals. I put some time into this question last week, and have the outlines of a proposal. Unfortunately, I'm on vacation right now and won't be back for a week. I plan to kick of an internals discussion with full details when I get back. Regardless, though, I don't think these broad considerations should block the RFC from being accepted -- rather, they should block final stabilization, by which point I'd like to have these questions resolved anyway. I would propose for now that we accept the RFC as-is, that is, with the new atomic types living alongside the existing ones. If and when we have a more complete plan for arch-specific APIs that would demand a different placement, we can move them prior to stabilization. (FWIW, the plan I have in mind would not involve moving these APIs) |
brson
reviewed
Apr 13, 2016
| #[cfg(target_has_atomic = "ptr")] | ||
| struct AtomicUsize {} | ||
| #[cfg(target_has_atomic = "ptr")] | ||
| struct AtomicPtr<T> {} |
This comment has been minimized.
This comment has been minimized.
brson
Apr 13, 2016
Contributor
AtomicPtr, Isize and Usize can't be defined cfg(target_has_atomic) because it's not backwards compatible. Please change this before landing.
This comment has been minimized.
This comment has been minimized.
jmesmon
Apr 14, 2016
@brson are you stating that because we've previously promised support for atomic ptr-size values, we're stuck supporting them for all platforms rust targets for the foreseeable future? In effect: if we ever want to support a platform without pointer-size atomics, it will require compiler-rt provide some AtomicPtr work-alike?
As the RFC notes later, all architectures we currently support would be unaffected. Only new, not yet existent archs would be broken. I'm presuming you don't think this is sufficient?
Is there some other process you're implicitly proposing here that I'm missing?
This comment has been minimized.
This comment has been minimized.
brson
Apr 14, 2016
Contributor
@jmesmon I think the ship has sailed on supporting ptr-sized atomics conditionally. We already guarantee support on platforms that don't have atomics.
This is a pretty bad situation though because our atomics are not guaranteed to be lock free.
Saving grace may be that no architectures without atomics are tier-1 so maybe we can roll back these prior decisions.
This comment has been minimized.
This comment has been minimized.
Amanieu
Apr 14, 2016
Author
Contributor
Saving grace may be that no architectures without atomics are tier-1 so maybe we can roll back these prior decisions.
To be precise all of the targets in tier 1, 2 and 3 support atomic operations. The only way to have a target that doesn't support atomics is through a custom target spec. And even in these cases I think attempts to use atomic operations will result in a linker error due to missing functions in compiler-rt (compiler-rt uses spinlocks to implement atomic operations, which themselves require support for atomic operations).
This comment has been minimized.
This comment has been minimized.
jmesmon
Apr 14, 2016
•
Saving grace may be that no architectures without atomics are tier-1 so maybe we can roll back these prior decisions
Isn't this exactly what the RFC says (which I referred to previously)? Is there something I'm missing here? Or is this just an agreement? Maybe you're just noting that this is something that needs discussion?
This comment has been minimized.
This comment has been minimized.
Amanieu
Apr 15, 2016
Author
Contributor
So I ran a few tests, and here's the current situation for platforms that don't support atomic types (I tested with ARMv6-M):
- LLVM will convert
fetch_addinto a call to__sync_fetch_and_add_4, which is normally provided by compiler-rt. - However compiler-rt itself uses atomic operations to implement
__sync_fetch_and_add_4, so it fails to compile on such platforms anyways.
So basically, atomic operations on those platforms has never worked in the first place, so we really aren't breaking anything.
brson
referenced this pull request
Apr 14, 2016
Closed
Current atomics are not defined to be lock-free #32967
This comment has been minimized.
This comment has been minimized.
|
@sunfish mentioned to me that it's possible to define lock-free atomics for any size less than a pointer as long as you are willing to spin on the full word. This says to me that maybe it's not necessary to define things like |
This comment has been minimized.
This comment has been minimized.
jmesmon
commented
Apr 14, 2016
•
|
@brson regarding spinning on smaller than atomic values, take a look at the discussion started in this comment above: #1543 (comment) I believe the result is that unless we're able to control data layout in such a way to forbid smaller-than-atomics treated as atomics from occupying the same atomic-unit as non-atomic values, spinning is insufficient. |
This comment has been minimized.
This comment has been minimized.
|
The libs team discussed this RFC during triage yesterday and the decision was to merge. We decided that the location in These types will require some extra scrutiny before stabilization, but that doesn't necessarily need to block them landing in the standard library at all, so we felt it was ok to merge and implement for now to let experimentation on nightly with the types. Thanks again for the RFC @Amanieu and discussion everyone! |
alexcrichton
referenced this pull request
Apr 14, 2016
Open
Tracking issue for #[cfg(target_has_atomic = ...)] #32976
alexcrichton
merged commit 450afdd
into
rust-lang:master
Apr 14, 2016
This comment has been minimized.
This comment has been minimized.
|
Tracking issue: rust-lang/rust#32976 |
bors
added a commit
to rust-lang/rust
that referenced
this pull request
May 5, 2016
bors
added a commit
to rust-lang/rust
that referenced
this pull request
May 6, 2016
bors
added a commit
to rust-lang/rust
that referenced
this pull request
May 6, 2016
bors
added a commit
to rust-lang/rust
that referenced
this pull request
May 9, 2016
This comment has been minimized.
This comment has been minimized.
|
Follow up: I've opened a discuss thread to talk about how we want to handle these APIs in general. |
Amanieu commentedMar 14, 2016
Previous RFC: #1505
Relevant issues: #1125 rust-lang/rust#24564
Relevant discussion in internals
Rendered