Search code examples
c++multithreadingatomiccompare-and-swap

Atomically decrement data member of a union?


I have a struct in a union with uint64_t, providing access to two int32_t. I would like to decrement one of the struct members atomically. I came up with this:

class{

public:

void atomicallyDecrement_B(const int32_t decrementByThisValue){
    MyUnion newUnion;
    MyStruct currentStruct = _union._data;

    do{
        const int32_t currentB = currentStruct.b;
        const int32_t currentA = currentStruct.a;
        const int32_t newB = currentB - decrementByThisValue;

        newUnion._data.a = currentA;
        newUnion._data.b = newB;
    }
    while(!std::atomic_compare_exchange_weak(&_union._data, &currentStruct, newUnion._data));
}

private:
    struct MyStruct{
        int a;
        int b;
    };

    union MyUnion{
        MyUnion(){
            _data.a = 0;
            _data.b = 0;
        }

        MyStruct _data;
        uint64_t _atomic;
    } _union;
};

but it seems the first argument to atomic_compare_exchange_weak() must be an atomic type itself. Is there any way to perform this operation without changing the uint64_t data member to be std::atomic<uint64_t>?

I am using GCC 5.2


Solution

  • GCC has atomic operations as built-ins. They are described on this page.

    The operation you are looking for is

    type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)
    

    or

    bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
    

    These builtins perform an atomic compare and swap. That is, if the current value of *ptr is oldval, then write newval into *ptr.

    For Windows users the corresponding capability is

    LONG __cdecl InterlockedCompareExchange(
      _Inout_ LONG volatile *Destination,
      _In_    LONG          Exchange,
      _In_    LONG          Comparand
    );
    

    Which is described here

    Example using the bool form of the gcc intrinsic:

    do{
       int oldVal = protectedVal;
       int newVal = someFunction(oldVal);
    } while (__sync_bool_compare_and_swap(&protectedVal, oldVal, newVal);