13

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.

5
  • 1
    I don't think you can have an atomic<T&>. And the pointer version is wrong (the stored pointer itself will be atomic, not the pointed-to value). Commented Nov 15, 2012 at 14:37
  • No, this won't work at all without implementation-specific things. Commented Nov 15, 2012 at 14:44
  • 3
    Maybe it's time to convert it to std::atomic<int> for the port? Considering that long has different sizes on Windows and OSX, you will probably have to do something OS specific anyway. Commented Nov 15, 2012 at 15:03
  • Are you sure? iOS has 4-byte ints and longs, and I thought Win32 too. Then, what do you mean? Suppose I can use ints everywhere here. How would that help me? Commented Nov 15, 2012 at 16:40
  • If you could use std::atomic<int> or std::atomic<long> you could use atomic_fetch_add operation to increment variables. Take a look at my answer. Commented Nov 23, 2012 at 8:58

4 Answers 4

13

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.

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

1 Comment

I was afraid of that. You summarised my situation correctly. Thanks for your assessment.
5

__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.

3 Comments

Also note that the GCC built-in may or may not be available in clang.
@caps Under what conditions are they available? clang version, build-options, etc.
In my case it with Clang 3.7.0 on a CentOS 6 using std=c++14 and using gcc's standard library implementation. That last is probably the key bit, since I imagine that is where it is pulling the __atomic_add_fetch from. My IDE (which is running on OSX) does not recognize the function.
2

While this question is about C++11, it's worth providing a C++20 solution for future readers. There is now std::atomic_ref which is a standard way of accomplishing this goal.

#include <atomic> // note: this should ideally take a reference instead of a pointer for a cleaner API inline long InterlockedIncrement(long& p) { // (1) using operator overloads with std::memory_order::seq_cst return ++std::atomic_ref(p); // (2) using an explicit memory order // note: ++x can be implemented as (x++ + 1) return std::atomic_ref(p).fetch_add(1, std::memory_order::relaxed) + 1; } 

Comments

1

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

4 Comments

That needs an existing std::atomic variable.
Since he is porting the code from Windows he could take into account this and introduce atomic variables (long -> std::atomic<long>)
Of course he could, but that isn't the question he is asking. The question specifically says he want to be passed long* instead of std::atomic<long>.
Hm, sorry I missed this part: Here I want to use an existing long variable that the callers passes to me by address...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.