Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement an atomic increment of a pointer to an integer using C++11 <atomic>?

Tags:

c++

c++11

atomic

While porting some Windows C++ code to iOS, I need to provide an implementation of Win32's long InterlockedIncrement(long *p) call. This is easy enough using the functions defined in <libkern/OSAtomic.h>.

However, I am wondering whether it's possible to write it in a OS-agnostic way using just the C++11 facility, mainly <atomic>. I came up with this, which I am not sure accomplishes what I want:

inline long InterlockedIncrement(long* p)
{
    std::atomic<long&> atomicP(*p);
    return ++atomicP;
}

Does this work? Is that good enough? The two lines are not atomic, but the increment should be atomic, which is the key here.

All the examples of use for <atomic> that I found are different, where a std::atomic<T> is defined and used directly. Here I want to use an existing long variable that the callers passes to me by address. I couldn't find such an example.

Edit: Clang 3.2 (in Xcode 4.x) fails to compile ++atomicP with the error "cannot increment value of type std::atomic<long&>" (nor atomicP += 1 either).

What would be the correct way?

Edit again: a pointer implementation compiles...

inline long InterlockedIncrement(long* p)
{
    std::atomic<long*> atomicP(p);
    return ++(*atomicP);
}

But I'm afraid this doesn't work, since I don't increment an atomic type, but the value pointed by the pointer, which is not atomic.

like image 708
Jean-Denis Muys Avatar asked Nov 15 '12 14:11

Jean-Denis Muys


3 Answers

__atomic_add_fetch GCC extension

In GCC 4.8, the C++ standard library implements std::atomic::operator++ with the GCC built-in __atomic_add_fetch, so you could write:

inline long InterlockedIncrement(long* p)
{
    return __atomic_add_fetch(p, 1, __ATOMIC_SEQ_CST);
}

I'm not sure for clang, but there seem to be some options like __c11_atomic_fetch_add http://clang.llvm.org/docs/LanguageExtensions.html

As others mentioned, the argument p itself would have to be std::atomic for you to use only the standard library methods: converting the pointer into atomic does not help because the atomic pointer only acts on the pointer, not on what it points to.


Your example implementation is constructing a new atomic from a pointer each time. This is not the intended use of std::atomic and I do not believe it works how you would like.

To my knowledge, the only way to do what you are looking to do (remove dependence on InterlockedIncrement in a platform independent way) is to replace all declarations for variables that you currently are calling Win32 "interlock" calls on with std::atomic versions of them. Then, you can remove the interlocked calls and use regular value semantics to modify the variable atomically. This is more readable (and more maintainable in the future), anyway.

I understand you wish to leave existing (well tested) code in place but I don't think you can in your case.

like image 33
Sean Cline Avatar answered Oct 10 '22 05:10

Sean Cline


I believe you could use an atomic_fetch_add operation. Take a look at the example here.

like image 2
Robertas Avatar answered Oct 10 '22 04:10

Robertas