Reputation: 2671
Atomic newbie here. My code currently looks like this (simplified):
std::atomic<Object*> object;
void thread_a()
{
object.load()->doSomething(); // (1)
}
void thread_b()
{
Object* oldObject = object.load();
Object* newObject = new Object();
// Update new object accordingly...
while (!object.compare_exchange_weak(oldObject, newObject));
delete oldObject;
}
In words, my idea is to let thread_b
atomically swap the shared object (double-buffering), while thread_a
performs some work on it. My question: can I safely assume that the shared object will be "protected" against data races while thread_a
calls doSomething()
on it, as done in (1)?
Upvotes: 14
Views: 2522
Reputation: 26256
I do this quite often, but... with shared pointers, and it's lock-free!
There's a problem with your design, as Some Programmer Dude suggested in his answer. But if you do this with shared_ptr
, and your program logic allows it, you'll be fine.
The reason why this works with shared_ptr
is that your object will not be deleted by force, as long as it lives somewhere else. So, this is how you do it:
std::shared_ptr<Object> object;
void thread_a()
{
std::atomic_load(&object)->doSomething(); // (1)
}
void thread_b()
{
std::shared_ptr<Object> oldObject = std::atomic_load(&object);
std::shared_ptr<Object> newObject = std::make_shared<Object>();
// Update new object accordingly...
while (!std::atomic_compare_exchange_weak(object, oldObject, newObject));
}
EDIT: This atomic_load
is specialized for the shared_ptr
. This seems to have caused confusion in the comments: https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
Upvotes: 4
Reputation: 409146
The fetching of the pointer with load()
will be atomic, but the call to doSomething()
itself will not be atomic.
That means the pointers could be swapped after load()
is called but before doSomething()
is called (which means doSomething()
is called on the wrong and now deleted object).
Perhaps a mutex could be a better choice here?
Upvotes: 9