I was wondering, can only primitive data types be declared std::atomic in C++11? Is it possible, say, to declare a library class object to be "atomically" mutated or accessed?
For example, I might have
using namespace std::chrono;
time_point<high_resolution_clock> foo;
// setter method
void set_foo() {
foo = high_resolution_clock::now();
}
// getter method
time_point<high_resolution_clock> get_foo() {
return foo;
}
But, if these setter and getter methods are called in different threads, I think that may cause undefined behavior. It would be nice if I could declare foo something like:
std::atomic<time_point<high_resolution_clock>> foo;
...so that all operations on foo would be conducted in an atomic fashion. In the application for my project there are possibly hundreds of such foo variables declared across dozens of classes, and I feel it would be far more convenient to make the object mutating and accessing "atomic" so to speak, instead of having to declare and lock_guard mutexes all over the place.
Is this not possible, or is there a better approach, or do I really have to use a mutex and lock_guard everywhere?
Update:
Primitive data types in c Primitive or most fundamental data type in c can be categorized in three groups on the basis of its application: 1. Integral type number: char , int 2. Real type number: float , double 3. Void or nothing type: void
We recommend C11 defines a new _Atomic () type specifier. You can declare an atomic integer like this: The C++ version has the advantage that it can be implemented without any compiler modification. It's possible to implement versions of these templates using inline assembly for each of the operations.
C++ is a bit more explicit. You can use any type in the std::atomic<> template. If you had a class HugeThing containing 1MB of data, you could expect std::atomic<HugeThing> to work for atomic updates. The implementation may protect things that large with a mutex, or in any other way it chooses.
C11 defines a new _Atomic () type specifier. You can declare an atomic integer like this: C++11 moves this declaration into the standard library: The C++ version has the advantage that it can be implemented without any compiler modification. It's possible to implement versions of these templates using inline assembly for each of the operations.
atomic<>
is not restricted to primitive types. It is permitted to use atomic<>
with a type T
that is trivially copyable. From section 29.5 Atomic types of the c++11 standard (it also stated at std::atomic
):
There is a generic class template atomic. The type of the template argument T shall be trivially copyable (3.9).
If the objects for which atomic access is required cannot be used with atomic<>
then define new objects, containing the original object and a std::mutex
. This means the lock_guard<>
is used within the getter and setter only of the new thread safe object, and not littered throughout the code. A template
might be able to define the thread safety machinery required:
template <typename T>
class mutable_object
{
public:
mutable_object() : t_() {}
explicit mutable_object(T a_t) : t_(std::move(a_t)) {}
T get() const
{
std::lock_guard<std::mutex> lk(mtx_);
return t_;
}
void set(T const& a_t)
{
std::lock_guard<std::mutex> lk(mtx_);
t_ = a_t;
}
private:
T t_;
mutable std::mutex mtx_;
};
using mutable_high_resolution_clock =
mutable_object<std::chrono::time_point<
std::chrono::high_resolution_clock>>;
using mutable_string = mutable_object<std::string>;
mutable_high_resolution_clock c;
c.set(std::chrono::high_resolution_clock::now());
auto c1 = c.get();
mutable_string s;
s.set(std::string("hello"));
auto s1 = s.get();
Atomics are limited to trivially copyable classes (i.e. classes which have no custom copy constructor, and whose members are also trivially copyable).
This requirement has huge advantages for atomics:
The latter is particularly useful, as atomics are sometimes implemented using spinlocks, and it is highly desired to avoid unbounded tasks while holding a spinlock. If any constructor was allowed, implementations would tend to need to fall back on full blown mutexes, which are slower than spinlocks for very small critical sections.
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