6 ms·
(2010) The post is still accurate, but in 2011 C and C++ added atomics, which are a more portable alternative to uses of volatile for atomicity. They can be m
by comex 3y ago
(2010)
The post is still accurate, but in 2011 C and C++ added atomics, which are a more portable alternative to uses of volatile for atomicity. They can be more efficient in some cases than the locks suggested by the post, especially in CPUs with higher core counts. (Note that dual-core consumer CPUs were around by 2010 but had only existed for a few years. Linux only finished removing the Big Kernel Lock in 2011.)
- lelanthran 3y ago> The post is still accurate, but in 2011 C and C++ added atomics, which are a more portable alternative to uses of volatile for atomicity. Atomics and volatile solve different problems, though. Atomics ensure a read or write completes uninterrupted. Volatile ensures that a read or write is never optimised away. I think C11 atomics can be optimised away (for example, reading a value twice in a row might result in only a single actual read). Happy to be corrected, though.
- deleted 3y ago[deleted]
- comex 3y ago> Atomics and volatile solve different problems, though. Yep, that's why atomics are only an alternative to uses of volatile _for atomicity_. For the original use case of accessing hardware registers, volatile is still the correct choice. It is indeed possible for C11 atomics to be optimized, although interestingly, the three major compilers do very little such optimization. This paper [1] lists some optimizations that are implemented in LLVM and some that aren't; it's from 2015 but from some quick testing it seems like not much has changed since. [1] https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4455.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n44...
- binarycoffee 3y agoI have always wondered about that. If optimizing repeated atomic loads is indeed allowed, waiting for a signal by spinning on an atomic load could loop forever. Yet I have the feeling most people consider such code to be valid. Are they wrong?
- manwe150 3y agoAcquire loads (and stronger) are required not to loop forever, if they could be observable from another thread. It is mentioned in the linked paper.
- gpderetta 3y agoSome optimizations are allowed within the rules of the memory model. For example, for an atomic x: x=1 x=2 Can be changed to just x=2. Still forward progress and eventual visibility must be guaranteed.
- SuchAnonMuchWow 3y agoWhile its true that atomic can solve the issue of atomic operations (increment, compare and swap, ...) that volatile doesn't try to solve, it is also required to solve the issue that volatile tried but failed to solve correctly: you have no ordering guaranteed between volatile and non-volatile memory accesses (see problem no.5 of the article). In that way, atomic complete volatile instead of being orthogonal o it, because it provides the ordering semantic missing in volatile. And it doesn't replace it completely, because as you said, atomic accesses can still be optimized away. So in most use-cases of volatile, you actually want to declare your variables atomic+volatile along with the correct memory_order on your atomic operations.
- quelsolaar 3y agoC11 did add _Atomic, BUT, they are not more portable than using volatile. In C11 any type of any size can have an atomic qualifyer. That means you can have a 50 byte struct that is an atomic. No hardware has a 50 byte atomic instruction so that is not implementable using atomics. The standard gets around this by letting an implementation have a hidden mutex to guarantee that the operations will be atomic. The problem with this is Windows. Windows lets an application load dynamicaly and shared libraries (DLL). This breaks the C11 Atomic model. Let me illustrate using an example: Application A creates an atomic data structure, and the implementation creates a mutex for it. Application B does the same thing. Application A wants to share this data structute with dll X. It then has to share its mutex with the DLL sop that the DLL and application uses the same syncronization primitive. Now Application B wants to do the same thing, problem is DLL X cant use Application Bs Mutex, becaus it is required to use Apllication As mutex. C11s Atomics will never be implemented on Windows because they cant be! Besides, all major compilers do support intrinsic atomics using volatile, that are nearly identical, (and in some ways better understood) so thats what I recomend using. Linus has indicated that he thinks the C11 concurrent memory model is broken so the kernel will continiue to use volatile and intrincics.
- manwe150 3y agoOn Linux, applications are required to load the libatomic shared library to correctly implement the full standard (on some older platforms like ARM, this is true even for small values since the hardware didn’t have an exchange instruction). There are a lot of operations that atomic int can do that volatile can’t do or will do incorrectly (such as seq-cst ordering or exchange). Why is this impossible for Windows? That sounds like a compiler implementation flaw, not an OS level impossibility.
- quelsolaar 3y agoBecause Windows lets a process link to a library that has already been initialized by another process, so the process cant share its mutex during initialization, becaus the initialization has allready happend. volotile gives you some decired propperties when multi-threadding, it is observable and therfor order rependent, but it does not have release/aquire semantics and it is not required to syncronize changes to other processors, so you need to use atomic intrincics in conjunction with volatile types. Volatile alone is NOT enough to be thread safe.