Atomic swap in GNU C++

夙愿已清 提交于 2019-11-28 21:21:46

Generally don't use volatile when writing concurrent code in C/C++. The semantics of volatile are so close to what you want that it is tempting but in the end volatile is not enough. Unfortunately Java/C# volatile != C/C++ volatile. Herb Sutter has a great article explaining the confusing mess.

What you really want is a memory fence. __sync_lock_test_and_set provides the fencing for you.

You will also need a memory fence when you copy (load) the rt_data pointer to your local copy.

Lock free programming is tricky. If you're willing to use Gcc's c++0x extensions, it's a bit easier:

#include <cstdatomic>

std::atomic<Data*> rt_data;

Data* swap_data( Data* new_data )
{
   Data* old_data = rt_data.exchange(new_data);
   assert( old_data != new_data );
   return old_data;
}

void use_data( )
{
   Data* local = rt_data.load();
   /* ... */
}
Michael Ekstrand

Update: This answer is not correct, as I am missing the fact that volatile guarantees that accesses to volatile variables are not reordered, but provides no such guarantees with respect to other non-volatile accesses and manipulations. A memory fence does provide such guarantees, and is necessary for this application. My original answer is below, but do not act on it. See this answer for a good explanation in the hole in my understanding that led to the following incorrect response.

Original answer:

Yes, you need volatile on your rt_data declaration; any time a variable can be modified outside the flow of control of a thread accessing it, it should be declared volatile. While you may be able to get away without volatile since you're copying to a local pointer, volatile at least helps with documentation and also inhibits some compiler optimizations that can cause problems. Consider the following example, adopted from DDJ:

volatile int a;
int b;
a = 1;
b = a;

If it is possible for a to have its value changed between a=1 and b=a, then a should be declared volatile (unless, of course, assigning an out-of-date value to b is acceptable). Multithreading, particularly with atomic primitives, constitutes such a situation. The situation is also triggered with variables modified by signal handlers and by variables mapped to odd memory locations (e.g. hardware I/O registers). See also this question.

Otherwise, it looks fine to me.

In C, I would probably use the atomic primitives provided by GLib for this. They'll use an atomic operation where available and fall back to a slow-but-correct mutex-based implementation if the atomic operations are not available. Boost may provide something similar for C++.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!