1

I want to compare and exchange 3 atomic variable:

std::atomic<int> a;
std::atomic<int> expected;
std::atomic<int> new;

int expectedValue = std::atomic_load_explicit(&expected, std::memory_order_relaxed);
int newValue = std::atomic_load_explicit(&new, std::memory_order_relaxed);

std::atomic_compare_exchange_strong_explicit(
    &a, 
    &expectedValue, 
    newValue, 
    std::memory_order_relaxed, 
    std::memory_order_relaxed);

But if in between reading expected and new variables and comparing them to a, one another thread change theirs values, current thread will work by previous values, so i change code to it:

while(true)
{
    int expectedValue = std::atomic_load_explicit(&expected, std::memory_order_relaxed);
    int newValue = std::atomic_load_explicit(&new, std::memory_order_relaxed);

    std::atomic_compare_exchange_strong_explicit(
        &a, 
        &expectedValue, 
        newValue, 
        std::memory_order_relaxed, 
        std::memory_order_relaxed);

    int newExpectedValue = std::atomic_load_explicit(&expected, std::memory_order_relaxed);
    int newNewValue = std::atomic_load_explicit(&new, std::memory_order_relaxed);

    if(newExpectedValue == expectedValue && newNewValue == newValue)
        break;
}

Is my code correct? or is there a better way to do this?

MRB
  • 3,752
  • 4
  • 30
  • 44

2 Answers2

3

Your rewritten function can still give inconsistent results. What if expected changes after you load it into newExpectedValue, but before you check if newExpectedValue == expectedValue? What if new and expected change after you load expected, but before new?

This isn't how atomics are intended to be used. If you need to do an operation involving three variables atomically, you should be using a lock to serialize access during the operation. A mutex or spin lock would be more appropriate here.

Collin Dauphinee
  • 13,664
  • 1
  • 40
  • 71
  • I think in this case atomic compare and exchange successfully change `a` variable with appropriate values. it is after compare and exchange and it may happen on every compare exchange. my goal is to protect compare exchange with consistent values. your talk about mutex is correct but i am writing lock-free allocators and memory manager and i want it to be really lock-free – MRB Oct 26 '13 at 08:25
  • 1
    It really depends on what you're doing. You could also have a case where you load an old value of `expected`, then another thread changes both values, and you load a new value of `new`. After you do the compare and exchange, something changes `expected` back to the old value, but `new` still has a different value. – Collin Dauphinee Oct 26 '13 at 08:35
  • Search for "ABA problem" for more details on what dauphic is talking about. – Michael Burr Oct 26 '13 at 08:53
  • @dauphic at this moment i don't access to my code.I will edit my question to provide appropriate information. pls help me to solve problem ... :) – MRB Oct 26 '13 at 09:09
1

Why are expected and new atomic in the first place? Typically, you compute the new value somehow on some thread, and only that thread knows the new value, and does the compare_exchange. Similarly, the value of expected is the old value before that thread started it's computation - but that expected, old value is again only important for that one thread.

In short: expected and new should not be shared across threads.

Eamon Nerbonne
  • 47,023
  • 20
  • 101
  • 166
  • At this moment i don't access to my code.I will edit my question to provide appropriate information. pls help me to solve problem ... :) – MRB Oct 26 '13 at 09:10
  • @MohammadRB: The best way to solve the problem is to ensure `new` and `expected` are not shared across threads. If you can't: but **really** make sure that's the case: then you could use a lock. However, if you do so, that means using that lock not just here, but everywhere any of these variables is accessed. I strongly recommend you NOT do that, however, because it sounds to me like you don't have a complete understanding of the underlying system (not meant as a criticism: parallel stuff is tricky); and that this problem you have here is just one symptom. – Eamon Nerbonne Oct 26 '13 at 11:56