3

Or any way to implement?

Let's have an atomic:

std::atomic<int> val;
val = 0; 

Now I want to update val only if val is not zero.

if (val != 0) {
    // <- Caveat if val becomes 0 here by another thread.
    val.fetch_sub(1);  
}

So maybe:

int not_expected = 0;
val.hypothetical_not_compare_exchange_strong(not_expected, val - 1);

Actually the above also will not work because val may get updated between val - 1 and the hypothetical function.

Maybe this:

int old_val = val;
if (old_val == 0) {
    // val is zero, don't update val. some other logic.
} else {

    int new_val = old_val - 1;
    bool could_update = val.compare_exchange_strong(old_val, new_val);
    if (!could_update) {
        // repeat the above steps again.
    } 
}

Edit:

val is a counter variable, not related to destruction of an object though. It's supposed to be an unsigned (since count can never be negative).

From thread A: if type 2 is sent out, type 1 cannot be sent out unless type 2 counter is 0.

while(true) {
    if counter_1 < max_type_1_limit && counter_2 == 0 && somelogic:
        send_request_type1();
        counter_1++;

    if some logic && counter_2 == 0:
        send_request_type2();
        counter_2++;
}

thread B & C: handle response:

if counter_1 > 0:
     counter_1-- 
     // (provided that after this counter_1 doesn't reduce to negative)
else 
     counter_2--
themagicalyang
  • 2,493
  • 14
  • 21
  • 1
    Do I understand correctly that you want to "decrement the value if it's not zero"? – Mats Petersson Jun 13 '18 at 05:33
  • 1
    Yes. There are other threads decrementing/incrementing and doing logic based on that. – themagicalyang Jun 13 '18 at 05:56
  • 1
    Is this meant as a reference count, so when the number hits zero, the object is destroyed? Or are you trying to implement something like a semaphore, where the count defines how many things are waiting on the object (for example)? – Mats Petersson Jun 13 '18 at 06:11
  • 1
    Possible duplicate of [Lock-free "decrement if not zero"](https://stackoverflow.com/questions/47650290/lock-free-decrement-if-not-zero) – VLL Jun 13 '18 at 06:16
  • @MatsPetersson I have updated the usage of val. It's a counter variable, I require two. counter_1 and counter_2 as mentioned in the edit. – themagicalyang Jun 13 '18 at 06:23

1 Answers1

7

The general way to implement not available atomic operations is using a CAS loop; in your case it would look like this:

/// atomically decrements %val if it's not zero; returns true if it
/// decremented, false otherwise
bool decrement_if_nonzero(std::atomic_int &val) {
    int old_value = val.load();
    do {
        if(old_value == 0) return false;
    } while(!val.compare_exchange_weak(old_value, old_value-1));
    return true;
}

So, Thread B & C would be:

if(!decrement_if_nonzero(counter_1)) {
    counter_2--
}

and thread A could use plain atomic loads/increments - thread A is the only one who increments the counters, so its check about counter_1 being under a certain threshold will always hold, regardless of what thread B and C do.

The only "strange" thing I see is the counter_2 fixup logic - in thread B & C it's decremented without checking for zero, while in thread A it's incremented only if it's zero - it looks like a bug. Did you mean to clamp it to zero in thread B/C as well?


That being said, atomics are great and all, but are trickier to get right, so if I were implementing this kind of logic I'd start out with a mutex, and then move to atomics if profiling pointed out that the mutex was a bottleneck.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299