Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is pointer address swapping always an atomic operation in C++?

Regarding this question Is there cases where a 32-bit variable could not been properly-aligned and the answers provided, can I assume I can swap addresses without any side effect when working under the windows platform?

For instance:

struct Foo
{
    // whatever Foo can hold
};

struct Bar
{
    void buildFoo()
    {
        auto tmp = new Foo;

        // do some stuff on tmp, or not

        foo = tmp;
    }

    Foo* foo;
};

Now, what are the consequences of having some threads using foo through an instance of Bar, and other threads invoking Bar::buildFoo()?

like image 257
mister why Avatar asked Feb 26 '14 10:02

mister why


People also ask

Is pointer assignment atomic?

Depending on the compiler and the platform, the pointer assignment may or may not be atomic.

What is atomic operation in C?

Atomic operations are intended to allow access to shared data without extra protection (mutex, rwlock, …). This may improve: ● single thread performance ● scalability ● overall system performance.

Is std :: swap Atomic?

It is not atomic. Atomic operations are not cheap and 99% of the time you do not need the atomicity. There are (IIRC) some other means to get atomic operations but std::swap() is not one of them.

What is atomic operation in memory access?

An atomic operation is a read-modify-write sequence that is performed without interference from another requester. Like exclusive accesses in AXI, Atomic Transactions allow a requester to modify data in a particular region of memory, while ensuring that writes from other requestors do not corrupt the data.


2 Answers

The C++ Standard

No, concurrently modifying/accessing raw2 pointers are not guaranteed to be an atomic operation in c++.

The C++ standard says that a data race is present if one thread modifies a memory location and another one modifies/access the same memory location, and if such a data race is present the program suffers from undefined behavior.

[intro.multithread]

4) Two expressions evaluations conflict if one of them modifies a memory location (1.7) and the other one accesses or modifies the same memory location.

...

21) 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 dat race results in undefined behavior.

1.raw is in not being wrapped in std::atomic<Foo*>, or equivalent.


Implementation specific behaviour (windows 32/64bit)

Under windows it's guaranteed that read/writes to properly-aligned 32-bit variables are always atomic, as is stated by the article linked by the question/answer you have referenced earlier.

Access to properly aligned 64-bit variables are also atomic on 64-bit windows.

Interlocked Variable Access - http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx

Simple reads and writes to properly-aligned 32-bit variables are atomic operations. In other words, you will not end up with only one portion of the variable updated; all bits are updated in an atomic fashion.

However, access is not guaranteed to be synchronized. If two threads are reading and writing from the same variable, you cannot determine if one thread will perform its read operation before the other performs its write operation.


What does this mean?

The standard says one thing, while the documentation from microsoft says another.. which are we to trust and work with? It certainly depends on what we are doing.

If we are developing soley for the windows platform we could read up on what the used compiler guarantees in regards of code generation and go from there, but if we want to write code that might be compiled and run under different platforms the only thing to properly trust is the standard.


So when working under windows I can safely swap 32-bit variables?

If you by "swapping" mean an operation such as what is written in the snippet below the answer is No, but if you mean "to assign" the answer is Yes.

void swap (int& a, int& b) {
  int tmp = a;
  
  a = b;
  b = tmp;
}

int main () {
  int x = 1;
  int y = 2;

  swap (x, y);
}

Nothing in the above snippet (or the previously mentioned documentation) says that this will be an atomic operation, and when looking at the implementation of our swap we easily see that the operations aren't synchronized properly.

Reading/writing a single 32-bit variable is safe under windows, but in the above there's nothing that guarantees that both x and y doesn't have the value 2 when we are in the middle of our swap.

It is, however, guaranteed that x never will consist of 50% of the bytes in the previous x and 50% of the bytes in y, or similar.. the individiual writes are atomic.

like image 89
Filip Roséen - refp Avatar answered Nov 03 '22 00:11

Filip Roséen - refp


Setting foo may be atomic, but it's implementation specific. The reason for this is that most platforms write to a word sized address in a single instruction, and this is an atomic operation. However, there's no requirement that your platform store its pointers in a word sized location, although it'd be rare not to.

You also need to take into account instruction reordering and the fact that other threads may not see the write to foo for some time the location of the pointer may be held in the processor cache and not flushed to memory.

Given all this you're better of using the std::atomic types, or if you don't have access to C++11 then there should be some sort of interlocked read/write functions available.

like image 22
Sean Avatar answered Nov 02 '22 23:11

Sean