6

I am unsure about how the memory ordering guarantees of atomic variables in c++11 affect operations to other memory.

Let's say I have one thread which periodically calls the write function to update a value, and another thread which calls read to get the current value. Is it guaranteed that the effects of d = value; will not be seen before effects of a = version;, and will be seen before the effects of b = version;?

atomic<int> a {0};
atomic<int> b {0};
double d;

void write(int version, double value) {
    a = version;
    d = value;
    b = version;
}

double read() {
    int x,y;
    double ret;
    do {
        x = b;
        ret = d;
        y = a;
    } while (x != y);
    return ret;
}
6
  • There are no barriers in your code; why did you tag it with "memory-barriers"? Commented May 27, 2015 at 18:58
  • 1
    Because I thought the answer might be something like "this isn't correct, you need to use a memory-barrier". Commented May 27, 2015 at 19:28
  • 1
    You might want to reword what you're asking. The people responding to this question are being disgustingly pedantic, and likely causing more harm than good for those who are curious about this. Commented May 27, 2015 at 19:49
  • I think this is a really good question. The answer from Christophe totally makes no sense before he added the part after 'Nevertheless'. The whole point for C++ memory model is to specify "how regular, non-atomic memory accesses are to be ordered around an atomic operation." (en.cppreference.com/w/cpp/atomic/memory_order), and his answer totally missed this point and "being disgustingly pedantic" (by Collin Dauphinee above). Commented May 27, 2015 at 23:01
  • @CollinDauphinee disgustingly pedantic sounds like the expression of some frustration. You're free to constructively propose a less pedantic answer. Commented May 28, 2015 at 17:14

3 Answers 3

4

The rule is that, given a write thread that executes once, and nothing else that modifies a, b or d,

  • You can read a and b from a different thread at any time, and
  • if you read b and see version stored in it, then
    • You can read d; and
    • What you read will be value.

Note that whether the second part is true depends on the memory ordering; it is true with the default one (memory_order_seq_cst).

Sign up to request clarification or add additional context in comments.

2 Comments

If the reader sees the same version for a and b, am I not guaranteed to have read the value that was set with that version? From my understanding, I have that guarantee unless it is possible for the effects of the line d = value to be visible before a = version in a call to write.
@JeffreyDallaTezza The point here is that you cannot read d unless you know that the write has finished, and you do so by reading b and seeing that version has been stored in it. Your code reads from d unconditionally, which causes a data race and hence undefined behavior.
4

Your object d is written and read by two threads and it's not atomic. This is unsafe, as suggested in the C++ standard on multithreading:

1.10/4 Two expression evaluations conflict if one of them modifies a memory location and the other one accesses or modifies the same memory location.

1.10/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 data race results in undefined behavior.

Important edit:

In your non-atomic case, you have no guarantees about the ordering between the reading and the writing. You don't even have guarantee that the reader will read a value that was written by the writer (this short article explains the risk for non-atomic variables).

Nevertheless, your reader's loop finishes based on a test of the surrounding atomic variables, for which there are strong guarantees. Assuming that version never repeats between writer different calls, and given the reverse order in which you aquire their value:

  • the order of the d read compared to the d write can't be unfortunate if the two atomics are equal.
  • similarly, the read value can't be inconsistent if the two atomics are equal.

This means that in case of an adverse race condition on your non-atomic, thanks to the loop, you'll end-up reading the last value.

12 Comments

Thankfully, the standard never uses the word "unsafe" normatively. A better way to describe the OP's program would be "incorrect", or "broken".
Just because the evaluations "conflict" doesn't mean it's a data race (and hence UB). You need a better quote.
Why is it undefined behavior (which implies anything can happen) and no unspecified behavior (the order of red/write is unknown)? Note: referring to build in types.
@DieterLücking because 1.10/21 applies. b is red and written concurrently.
@JeffreyDallaTezza I've made an important edit: I focused to much on the race conditions on d that I didn't pay attention that you were reading a and b in the reverse order - resume at "Nevertheles".
|
0

Is it guaranteed that the effects of d = value; will not be seen before effects of a = version;, and will be seen before the effects of b = version;?

Yes, it is. This is because sequensial consistency barrier is implied when read or write atomic<> variable.

Instead of storing version tag into two atomic variables before value's modification and after it, you can increment single atomic variable before and after modification:

atomic<int> a = {0};
double d;

void write(double value)
{
     a = a + 1; // 'a' become odd
     d = value; //or other modification of protected value(s)
     a = a + 1; // 'a' become even, but not equal to the one before modification
}

double read(void)
{
     int x;
     double ret;
     do
     {
         x = a;
         ret = value; // or other action with protected value(s)
     } while((x & 2) || (x != a));
     return ret;
}

This is known as seqlock in the Linux kernel: http://en.wikipedia.org/wiki/Seqlock

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.