5 ms·
I'd kill for a "volatile" with guaranteed semantics in Rust. Possibly more than one person.
by legerdemain 5y ago
I'd kill for a "volatile" with guaranteed semantics in Rust. Possibly more than one person.
- steveklabnik 5y agoIs there a specific worry that this would solve, or is it just the general uncomfortableness with it not being a 100% guarantee?
- legerdemain 5y agoThe latter: I translate slightly off-the-beaten-path concurrency stuff to Rust, and a lot of papers and reference implementations lean on the semantics of "volatile" in C++ or Java.
- steveklabnik 5y agoCool, makes sense. Thanks :)
- NobodyNada 5y agoNote that “slightly off-the-beaten-path concurrency stuff” may not be correct in today’s world, especially if it uses ‘volatile’. This is primarily because memory accesses can now be reordered throughout the compiler/processor stack, from the optimizer down to out-of-order execution down to the memory hierarchy. In particular, C(++) guarantees that volatile reads/writes will never be reordered with one another, but they may be reordered with reads/writes to other variables [0] (and this ordering may not be consistent across CPU cores). Importantly, the x86 ISA provides much stronger guarantees than C/C++. AFAIK the only memory reordering allowed is that a write followed by a read can be swapped around. This is extremely strict (we can’t even reorder adjacent reads) and literally any other processor architecture will provide much weaker guarantees. And because C and C++ formally defined a weak memory model in 2011, modern compilers are free (even when targeting x86 chips) to perform optimizations that reorder memory accesses in ways programmers familiar with x86’s semantics might not expect. So an algorithm that worked on x86 processors 10-15 years ago may no longer be correct when compiled with a modern optimizing compiler, or when running on ARM. (If in doubt, you should be OK replacing anything volatile with an atomic and specifying sequentially-consistent ordering semantics for all operations — which you can do in Rust!) [0]: https://systemtbe.blogspot.com/2014/05/volatile-memory-barriers-and-loadstore.html?m=1 https://systemtbe.blogspot.com/2014/05/volatile-memory-barri...
- gpderetta 5y agoTo be clear, plain C volatile has never been sufficent even on x86. This has not changed with C++11/C11. For example the linux kernel (which has yet to embrace the C11 MM) has always used memory and compiler barriers were needed even on x86 for decades. Java volatile is different of course.
- jstimpfle 5y agoAs long as you use C's volatile for all the variables that are involved in a (lock-free) transaction, you should be fine I think. For example, volatile is said to be sufficient for interfacing with hardware through memory mapped regions, because that is one area where it is very clear what the outputs are and one can just make all those accesses volatile so they won't get reordered. Although, having to go all-volatile is maybe too much of a constraint, when many algorithms (like lock-free queues etc.) only require like 3 non-reordered accesses (2 to begin, 1 to end?) to safeguard a transaction, and then the remaining reads and writes can be reordered arbitrarily (inside the safe bounds).
- adwn 5y ago> As long as you use C's volatile for all the variables that are involved in a (lock-free) transaction, you should be fine I think. No, you really won't, neither in C nor in Rust. There are (at least) two reasons for that: 1) You can't do atomic read-modify-write operations (like fetch_add) with volatile. 2) "volatile" is meant for the compiler, not for the processor. Volatile accesses are compiled down to regular read and write machine instructions, so you won't get any ordering guarantees for code running concurrently on different cores. The reason "volatile" is useful for memory-mapped IO is that such memory is mapped with different flags for cacheability than regular memory. For example, given the right flags, the processor won't cache stores in a store-buffer, or won't coalesce smaller stores into larger stores before sending them up the memory hierarchy.
- jstimpfle 5y ago> You can't do atomic read-modify-write operations (like fetch_add) with volatile. Are those operations so important? All I've ever done to be honest is implement some lock-free SRSW queues, and those can be easily implemented with just acquire/release semantics. No atomic read-modify-write needed. Could it be that those are only needed when more than 2 threads are involved?
- masklinn 5y agoAFAIK Rust volatile accesses follow C++ semantics, no? Meaning they’re completely different and way incompatible with Java’s.
- exDM69 5y ago> semantics of "volatile" in C++ or Java But the semantics of "volatile" are completely different in C++ and Java! And I've seen lots of bugs from people assuming Java semantics for volatile in C++. In C++ land, volatile is mostly useless and sometimes harmful (see [0]). It's dangerous to use it in concurrent code and hardly ever useful for memory mapped i/o without appropriate memory barriers (which make the volatile qualifier redundant). In Java land, volatile can actually provide some useful guarantees when it comes to multithreaded uses but afaik they will usually just be turned into atomic load/store instructions so you might as well be writing atomics explicitly. And it can be debated whether "volatile" should be a type qualifier in the first place. I would argue that it's better to make load/store operations on volatile memory explicit (for those very rare cases when you need it). There is experimental support in Rust for the latter, `volatile_load` and `volatile_store` in `std::intrinsics` [1]. But still, doing volatile loads and stores in multithreaded code is hardly ever the right thing to do, at least without accompanying them with the relevant memory barriers (which are especially important on ARM CPUs). [0] https://github.com/torvalds/linux/blob/master/Documentation/process/volatile-considered-harmful.rst https://github.com/torvalds/linux/blob/master/Documentation/... [1] https://doc.rust-lang.org/std/intrinsics/fn.volatile_store.html https://doc.rust-lang.org/std/intrinsics/fn.volatile_store.h...
- tialaramex 5y ago> There is experimental support in Rust for the latter It's not really experimental. You've linked the underlying core intrinsic, but the stabilised interface, which is what programmers should actually use, is std::ptr::write_volatile / std::ptr::read_volatile Essentially you linked an implementation detail and that's why it's labelled unstable and you can't use it in stable Rust.
- exDM69 5y agoI see, thanks for the clarification. My search engine pointed me to the std::intrinsics as first hit. But yeah, you can have your volatile in Rust then. It's not a type level annotation, but an explicit (unsafe) operation.
- 5y ago
- tialaramex 5y agoRust provides volatile load and store intrinsics which do exactly what they say on the box, but given you talk about "concurrency stuff" I expect they're completely irrelevant to you because this (older) meaning of volatile is about hardware memory access (e.g. for device drivers) and nothing to do with concurrency. Java's "volatile" is sequential consistency on steroids, a promise that changes to a volatile variable "happen-before" anything else that thread does, and "happen-after" anything the thread already did. Rust's std::sync::atomic has a hardware fence which you could use to deliver this same outcome (throw away performance, gain sequential consistency). For concurrency C++ volatile does not supply any semantics, except on MSVC. If you're looking at papers or reference implementations which assume C++ volatile has semantics that are relevant to concurrency you're probably looking at papers that only needed Acquire-release and it worked on the x86 CPU they tried it with, or they forgot to mention it only works on MSVC (which promises Acquire-release even on platforms other than x86).
- steveklabnik 5y ago> which do exactly what they say on the box I mean, the box says: https://doc.rust-lang.org/stable/std/ptr/fn.read_volatile.html#notes https://doc.rust-lang.org/stable/std/ptr/fn.read_volatile.ht... > Rust does not currently have a rigorously and formally defined memory model, so the precise semantics of what “volatile” means here is subject to change over time. That being said, the semantics will almost always end up pretty similar to C11’s definition of volatile. Which isn't exactly "exact", you know?
- tialaramex 5y agoIt's not. But I feel confident (perhaps my confidence is misplaced) that this is because what's really going on here is that this does what LLVM does, and there's some risk LLVM changes exactly what it does, most likely to suit clang so in that case Rust has to go along for the ride or perhaps get itself a new suite of compiler backends... If so I feel that risk is small and continues to shrink.
- steveklabnik 5y ago
- scatters 5y agoWhat does "volatile" have to do with concurrency? It's for hardware access and benchmarking, not for multithreading.
- volta83 5y ago> I'd kill for a "volatile" with guaranteed semantics in Rust. Possibly more than one person. The reason people ask for this is because they haven't thought this trough. Even if the compiler "preserves" all volatile operations, the HW does not. A trivial example is WASM. Rust can generate WASM code that, when using volatile, has many duplicated loads and stores in some order. Then the browser runs a WASM optimizer on the WASM code, which discovers that these instructions are redundant, and there you go, your program now doesn't do what you wanted anymore (formally, it never did). x86, ARM, PPC, FPGAs, GPUs, etc. all do this in HW as well. So while you could argue that, e.g., WASM should have volatile loads and stores, the same argument can be made at the next level down below, down to hardware. You'd need to ask every hardware vendor for some sort of serializing loads and stores, and people have done that, and vendors have implemented that, but then when users finally got what they wanted, they complained that they are too slow, so at that point they just stop using volatile and use atomics which is what they should have done in the first place.
- legerdemain 5y agoThank you, but I'm aware of this. Why did you claim that I haven't thought this through?
- volta83 5y agoYour original comment reads like a feature request. Since now I know that you knew that the feature you were requesting is impossible to implement, and you did not mention that in your feature request, now... i have no idea of what the goal of your comment was.