Are C/C++ fundamental types, like int
, double
, etc., atomic, e.g. threadsafe?
Are they free from data races; that is, if one thread writes to an object of such a type while another thread reads from it, is the behavior well-defined?
If not, does it depend on the compiler or something else?
The type sig_atomic_t is always an integer data type, but which one it is, and how many bits it contains, may vary from machine to machine. Data Type: sig_atomic_t. This is an integer data type. Objects of this type are always accessed atomically. In practice, you can assume that int is atomic.
C was designed to run on a simple serial computer. But increasing numbers of machines have more than one CPU, and low-level code (the kind still written in C) has to work around this issue.
An atomic datatype is a basic datatype such as a Boolean, string, integer, decimal, or date. To define custom atomic datatypes, add restrictions to an atomic datatype to limit the content.
What is an atomic operation in C? Neither of them are, although it is quite rare for the assignment to not be atomic. You have to document the core and compiler to get a useful answer.
No, fundamental data types (e.g., int
, double
) are not atomic, see std::atomic
.
Instead you can use std::atomic<int>
or std::atomic<double>
.
Note: std::atomic
was introduced with C++11 and my understanding is that prior to C++11, the C++ standard didn't recognize the existence of multithreading at all.
As pointed out by @Josh, std::atomic_flag
is an atomic boolean type. It is guaranteed to be lock-free, unlike the std::atomic
specializations.
The quoted documentation is from: http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf. I'm pretty sure the standard is not free and therefore this isn't the final/official version.
- Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one reads or modifies the same memory location.
- The library defines a number of atomic operations (Clause 29) and operations on mutexes (Clause 30) that are specially identified as synchronization operations. These operations play a special role in making assignments in one thread visible to another. A synchronization operation on one or more memory locations is either a consume operation, an acquire operation, a release operation, or both an acquire and release operation. A synchronization operation without an associated memory location is a fence and can be either an acquire fence, a release fence, or both an acquire and release fence. In addition, there are relaxed atomic operations, which are not synchronization operations, and atomic read-modify-write operations, which have special characteristics.
- Two actions are potentially concurrent if
(23.1) — they are performed by different threads, or
(23.2) — they are unsequenced, and at least one is performed by a signal handler.
The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other, except for the special case for signal handlers described below. Any such data race results in undefined behavior.
- There shall be explicit specializations of the atomic template for the integral types ``char,
signed char
,unsigned char
,short
,unsigned short
,int
,unsigned int
,long
,unsigned long
,long long
,unsigned long long
,char16_
t,char32_t
,wchar_t
, and any other types needed by the typedefs in the header<cstdint>
. For each integral type integral, the specializationatomic<integral>
provides additional atomic operations appropriate to integral types. There shall be a specializationatomic<bool>
which provides the general atomic operations as specified in 29.6.1..
- There shall be pointer partial specializations of the atomic class template. These specializations shall have standard layout, trivial default constructors, and trivial destructors. They shall each support aggregate initialization syntax.
- Operations on an object of type atomic_flag shall be lock-free. [ Note: Hence the operations should also be address-free. No other type requires lock-free operations, so the atomic_flag type is the minimum hardware-implemented type needed to conform to this International standard. The remaining types can be emulated with atomic_flag, though with less than ideal properties. — end note ]
Since C is also (currently) mentioned in the question despite not being in the tags, the C Standard states:
5.1.2.3 Program execution
...
When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects that are neither lock-free atomic objects nor of type
volatile sig_atomic_t
are unspecified, as is the state of the floating-point environment. The value of any object modified by the handler that is neither a lock-free atomic object nor of typevolatile sig_atomic_t
becomes indeterminate when the handler exits, as does the state of the floating-point environment if it is modified by the handler and not restored to its original state.
and
5.1.2.4 Multi-threaded executions and data races
...
Two expression evaluations conflict if one of them modifies a memory location and the other one reads or modifies the same memory location.
[several pages of standards - some paragraphs explicitly addressing atomic types]
The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.
Note that values are "indeterminate" if a signal interrupts processing, and simultaneous access to types that are not explicitly atomic is undefined behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With