is int assignment atomic in g++?

Hi,

I need to wrote a data structure that is safe to write and read in a program with one writer and one reader threads. The reader can miss data but the data it reads out needs to be as updated as possible and not corrupted. No mutex or other lock mechanisms are to be used. Here is what I come up with:

template <typename Data>
class SafeData
{
public:

SafeData() : read_(0), write_(1), last_(0){ }

inline const Data& read()
{
read_ = last_;
return data_[read_];
}

inline void write(const Data& data)
{

write_ = (last_ + 1) % 3;
int temp = read_;
if (write_ == temp)
write_ = (temp + 1) % 3;

data_[write_] = data;
last_ = write_;
}

private:
Data data_[3];
int write_;
int last_;
int read_;
};

and I test it with the following program:

#include <iostream>
#include <pthread.h>
#include <stdlib.h>
#include "SafeData.hpp"

using namespace std;

struct SS
{
SS(int v = 0) : seed(v), square(v * 2) {}

bool ok() const
{
return square == seed * 2;
}

int seed;
int square;
};

SafeData<SS> data;

void *read(void *arg)
{
for (int i = 0; i < 100000000; ++i)
{
SS r = data.read();
if (!r.ok())
{
cout << "corrupted data: " << r.seed << " " << r.square << endl;
exit(-1);
}
}

pthread_exit(NULL);
}



int main(int argc, const char* argv[])
{

pthread_t r_thread;
int ret = pthread_create(&r_thread, NULL, &read, NULL);
if (ret != 0)
{
cerr << "failed to create read thread\n";
exit(-1);
}


for (int i = 0; i < 100000000; ++i)
{
SS w(i);
data.write(w);
}

return 0;
}

My assumption here is that assignment of int is atomic. When I compile it with g++ without optimization, it seemed to work fine. No corrupted data encountered. However, as soon as I use optimization, i.e., g++ -O, I started to see corrupted data randomly.

I don't understand what exactly is going on here. Can someone shed some light here?

Thanks,

Frank
No, int updates are not guaranteed to be atomic if atomic operations are not explicitly used.
> I don't understand what exactly is going on here. Can someone shed some light here?

A simple example of out of order memory operations:
https://en.wikipedia.org/wiki/Memory_barrier#An_illustrative_example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <atomic>

void not_atomic( int& v ) 
{ 
    v = 22 ; 
    /*
        mov DWORD PTR [rdi], 22
        ret
    */
} 

void atomic( std::atomic<int>& v ) 
{ 
    v = 22 ; 
    /*
        mov DWORD PTR [rdi], 22
        mfence
        ret
    */
}

https://godbolt.org/g/psXYFl
Topic archived. No new replies allowed.